NBG Rates
nbg-rates-mcp
Ask Claude, or any other MCP client, for the official National Bank of Georgia (NBG) rate of the lari (GEL) on any date, or to convert an amount at that rate. nbg-rates-mcp runs on your machine or as a hosted connector you paste into claude.ai or ChatGPT, needs no account and no key, and encodes the rules a raw lookup gets wrong: per-unit values, Tbilisi calendar days, an explicit flag when a day has no rate of its own, and an error, never a stale rate, for a date NBG has not published yet.
nbg-rates.env.ge: what it does and how to add it, in English and Georgian.
This is an independent open-source project, not affiliated with the National Bank of Georgia. NBG's website, nbg.gov.ge, is the source of truth. The NBG endpoints this server reads are undocumented and may change.
What you can ask
What was the USD rate on 30 September 2026?
Convert 1,500 USD to GEL at the NBG rate of 27 September 2026.
Give me the EUR rate for every day of September 2026 as a table.
How many US dollars is 1 euro today, at NBG rates?
Write the date with the month as a word, "10 September 2026": 10/09/2026 reads as two different dates.
For the second question the answer is: 27 September 2026 is a Sunday, so the rate in force is the one NBG set for Saturday 26 September, 2.6080 GEL per USD, still valid on Sunday; 1,500 USD = 3,912.00 GEL, and the answer says the rate is carried over from the 26th.
In Claude Desktop the "+" menu of a chat also lists four ready-made questions from this server: today's rates, the rate on a date, converting an amount, and a rates table for a month.
Related MCP server: plinth-fx-mcp
Which rate you get
Georgian accounting and tax reporting use the official NBG rate in force on the date of the transaction. This server returns exactly that rate, the one shown on nbg.gov.ge for the day you name.
Converted amounts come rounded half up to two decimals (resultRounded), the way a ledger posts them, computed exactly from NBG's published figures.
NBG sets rates on business days around 17:00 Tbilisi time, valid from the next calendar day. A Sunday, a Monday or the day after a public holiday has no rate of its own and keeps the last one set; the answer says so (carriedOver) and names the day that rate took effect. A date NBG has not published yet, including tomorrow before about 17:00 Tbilisi time, is answered with an error, never a guess.
Install
claude.ai, Claude mobile and ChatGPT (nothing to install)
Paste this URL as a custom connector:
https://nbg-rates.env.ge/mcpOn claude.ai open Customize, then Connectors, then "+ Add" and "Add custom connector", paste the URL and finish with "Add" (on a Team or Enterprise plan an owner adds it under Organization settings, then Connectors); the connector then also appears in the Claude mobile apps and in Claude Desktop under the same account. In ChatGPT open chatgpt.com/plugins, select the plus button, then "Add custom MCP server", paste the URL under Connection, leave authentication off and finish with "Create as a plugin" (in a workspace, an admin may need to allow custom MCP servers first). The landing page, nbg-rates.env.ge, has step-by-step instructions for each app.
This hosted copy is the latest release of this server, run by the maintainer on AWS. It needs no account and never records the amounts, dates or currencies asked. It keeps two logs for 30 days: which tool was called from which client and whether NBG answered, and the gateway's access log (time, path, status, and the caller's IP address and user agent; for claude.ai and ChatGPT the caller is Anthropic's or OpenAI's server). The logs are used only to keep the service running, to see which clients use it and to investigate errors or abuse; they are not sold, shared or used for advertising. The landing page sets no cookies and loads nothing from anyone else.
Claude Desktop (macOS, Windows)
Download nbg-rates-mcp-latest.mcpb and open it. Claude Desktop installs the extension and runs it with its own Node.js, so nothing else is needed. The same file, under its version number, is attached to every release.
Claude Desktop (Linux beta) and manual configuration
Add the server to claude_desktop_config.json. The file may not exist yet; create it with the snippet below as its whole content, then restart Claude Desktop.
Platform | Location of |
macOS |
|
Windows |
|
Linux |
|
{ "mcpServers": { "nbg-rates": { "command": "npx", "args": ["-y", "nbg-rates-mcp"] } } }This needs Node.js 22 or later; see Node.js below.
Claude Code
claude mcp add nbg-rates -- npx -y nbg-rates-mcpTo make the server available in every project, add it with user scope:
claude mcp add --scope user nbg-rates -- npx -y nbg-rates-mcpCursor and other clients
Add the same mcpServers entry to the client's MCP settings. In Cursor that is ~/.cursor/mcp.json for every project, or .cursor/mcp.json inside one project.
Node.js
npx needs Node.js 22 or later (the Claude Desktop bundle does not). Check with node --version, and install it if needed:
macOS (Homebrew):
brew install nodeDebian and Ubuntu (NodeSource, Node.js 24):
curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh sudo -E bash nodesource_setup.sh sudo apt-get install -y nodejsOther Linux distributions (nvm; open a new terminal after the first line):
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash nvm install --ltsWindows (PowerShell):
winget install OpenJS.NodeJS.LTSWSL: the same commands as Debian and Ubuntu, run inside the WSL shell.
The rest of this document is for developers and for people writing instructions for a model.
Self-hosting
The same server speaks Streamable HTTP: nbg-rates-mcp --http --port 3000 serves http://127.0.0.1:3000/mcp (add --host 0.0.0.0 to listen on every interface). On AWS Lambda the handler is dist/lambda.handler of the npm package with its production dependencies zipped beside it, behind an API Gateway HTTP API with payload format 2.0 and the $default stage (a named stage prefixes every path with its name, so each request would get a 404). Set NBG_RATES_HTTP_HOSTS to the public host name in either case, so the endpoint answers only for it.
Tools
Every tool is read-only. Dates are calendar days in Tbilisi in the form YYYY-MM-DD, and every rate is GEL per one unit of the currency. Each tool returns its result as structured content and as the same JSON in a text block.
nbg_get_rates
The official NBG rates in force on one calendar date. NBG quotes some currencies per 10, 100, 1000 or 10000 units; the rate is always divided down to one unit, and the raw pair is kept as nbgQuantity and nbgRate.
Inputs:
Name | Required | Description |
| no | Calendar date. Defaults to today in Tbilisi. Tomorrow's rate exists after about 17:00 Tbilisi time. |
| no | ISO 4217 codes to return, case-insensitive, duplicates ignored, for example |
| no | Language of the currency names: |
Output:
requestedDate: the date asked for, or today in Tbilisi whendateis omitted.effectiveDate: the date the returned rates took effect, as NBG publishes it.carriedOver:truewhen the rates in force onrequestedDatetook effect on an earlier day. They are still the official rates forrequestedDate; quoteeffectiveDatealongside them.rates: one entry per currency withcode,name,rate(GEL per one unit),diff(change versus the previous published rate, per one unit),nbgQuantity(the units NBG quotes the raw rate for: 1, 10, 100, 1000 or 10000) andnbgRate(the raw rate as NBG publishes it, fornbgQuantityunits). Values below 0.000001, mostly thediffof a currency quoted per 1000 or 10000 units, are JSON numbers in exponent notation, for example4e-7.unknownCodes: requested codes NBG did not quote on that date, and inputs that are not a three-letter currency code at all. The other requested codes are still answered.GELis listed here too: every rate is in GEL, so the table has no row for it.
Worked example: on Monday 2026-10-05 a user asks for the AMD rate on Sunday 2026-10-04. NBG sets no rate for a Sunday, so the rate in force is the one valid from Saturday 2026-10-03, which NBG quotes as 7.1762 GEL per 1000 drams. The request
{ "date": "2026-10-04", "currencies": ["AMD"] }returns
{
"requestedDate": "2026-10-04",
"effectiveDate": "2026-10-03",
"carriedOver": true,
"rates": [
{
"code": "AMD",
"name": "Armenian Dram",
"rate": 0.0071762,
"diff": 0.000012,
"nbgQuantity": 1000,
"nbgRate": 7.1762
}
],
"unknownCodes": []
}The same request for Monday 2026-10-05 returns the same rate with requestedDate 2026-10-05: the rate NBG set on Friday for Saturday is still in force on Monday.
nbg_convert
Converts an amount between two currencies with the official NBG rate in force on a calendar date. Either side may be GEL; a pair without GEL is converted through GEL.
Inputs:
Name | Required | Description |
| yes | Amount in the |
| yes | ISO 4217 code, or |
| yes | ISO 4217 code, or |
| no | Calendar date whose official rate to apply. Defaults to today in Tbilisi. |
Output:
amount,from,to: the request, with the codes in upper case.result: the converted amount, unrounded, for further calculation.resultRounded: the exact amount rounded half up to two decimals, computed from NBG's published figures; the amount to show. JSON drops trailing zeros, so 3.90 arrives as3.9.rate: one unit offromexpressed into.via:directwhen either side is GEL (or both codes are the same),GELwhen a pair without GEL is converted through GEL.requestedDate,effectiveDate,carriedOver: as innbg_get_rates.
GEL to GEL is the identity and needs no NBG rate, so it answers for any valid date. A code NBG did not quote on the date is an error.
nbg_convert_many
Converts a list of amounts, each at the official NBG rate in force on its own date: one call for a list of invoices. Items are answered independently, so a bad date or code fails only its own item. The server makes at most one NBG request per distinct date, one date after another; once NBG stops answering, the dates not yet asked fail with the same error without a request.
Inputs:
Name | Required | Description |
| yes | 1 to 100 entries of |
An empty list, more than 100 items, or an item without the shape { amount, from, to, date? } (a missing or non-numeric amount, a missing code, or a text over 64 characters) fails the whole call at the input schema; every other problem fails only its item.
Output:
items: one entry per input, in input order. A converted item is thenbg_convertoutput, withresultRounded. A failed item is{ amount, from, to, date, error }: the item as given, withoutdatewhen it had none, anderror, the message that says what went wrong (see Errors).converted,skipped: how many items were converted and how many failed.total:{ to, resultRounded }, present only when at least one item was converted, every converted item has the same target currency, and the sum is a finite number: the sum of theirresultRoundedvalues, computed exactly, the way a ledger sums posted lines. It covers the converted items only, so it is partial whenskippedis not 0.
Worked example: 10 USD and 1.5 USD to GEL on 2026-10-10, and 100 USD to GEL on 2026-13-01, a date that does not exist. The request
{
"items": [
{ "amount": 10, "from": "USD", "to": "GEL", "date": "2026-10-10" },
{ "amount": 1.5, "from": "USD", "to": "GEL", "date": "2026-10-10" },
{ "amount": 100, "from": "USD", "to": "GEL", "date": "2026-13-01" }
]
}returns
{
"items": [
{
"amount": 10,
"from": "USD",
"to": "GEL",
"result": 26.005,
"resultRounded": 26.01,
"rate": 2.6005,
"via": "direct",
"requestedDate": "2026-10-10",
"effectiveDate": "2026-10-10",
"carriedOver": false
},
{
"amount": 1.5,
"from": "USD",
"to": "GEL",
"result": 3.9007499999999995,
"resultRounded": 3.9,
"rate": 2.6005,
"via": "direct",
"requestedDate": "2026-10-10",
"effectiveDate": "2026-10-10",
"carriedOver": false
},
{
"amount": 100,
"from": "USD",
"to": "GEL",
"date": "2026-13-01",
"error": "2026-13-01 is not a valid calendar date (not a real calendar date); use YYYY-MM-DD."
}
],
"converted": 2,
"skipped": 1,
"total": { "to": "GEL", "resultRounded": 29.91 }
}The total, 26.01 + 3.9 = 29.91 GEL, covers the two converted items; skipped is 1, so it is partial.
nbg_list_currencies
Lists every currency NBG publishes a GEL rate for today. NBG quoted other currencies in the past, so a code missing here may still have historical rates.
Inputs:
Name | Required | Description |
| no | Language of the currency names: |
Output:
effectiveDate: the date today's rates took effect.currencies: one entry per currency withcode,nameandnbgQuantity(the units NBG quotes the raw rate for).
nbg_rate_history
The official rate of one currency, in GEL per one unit, in force on every calendar day of an inclusive range. One call makes one request to NBG.
Inputs:
Name | Required | Description |
| yes | ISO 4217 code, for example |
| yes | First calendar day, inclusive. |
| yes | Last calendar day, inclusive. At most 366 days including |
Output:
currency,from,to: the request, with the code in upper case.days: one entry per calendar day. A day whose own rate is in force is{ date, rate }, withratein GEL per one unit. A carried-over day addseffectiveDate, the day its rate took effect, andcarriedOver: true; an absenteffectiveDatemeans the rate took effect ondateitself. A full year stays near 6,000 tokens.summary:first,last,change(last minus first),minandmaxwithminDateandmaxDate(first occurrence), andaverage, the arithmetic mean over every calendar day of the range, carried-over days included.changeandaverageare exact, rounded half up to the decimals NBG's quantity gives the currency, 4 + log10(nbgQuantity), the largest over the days (4 for USD, 7 for AMD).
For example, USD from Friday 2026-10-02 to Tuesday 2026-10-06 (Saturday has its own rate; Sunday and Monday carry it):
{
"currency": "USD",
"from": "2026-10-02",
"to": "2026-10-06",
"days": [
{ "date": "2026-10-02", "rate": 2.6042 },
{ "date": "2026-10-03", "rate": 2.6039 },
{ "date": "2026-10-04", "rate": 2.6039, "effectiveDate": "2026-10-03", "carriedOver": true },
{ "date": "2026-10-05", "rate": 2.6039, "effectiveDate": "2026-10-03", "carriedOver": true },
{ "date": "2026-10-06", "rate": 2.6032 }
],
"summary": {
"first": 2.6042,
"last": 2.6032,
"change": -0.001,
"min": 2.6032,
"minDate": "2026-10-06",
"max": 2.6042,
"maxDate": "2026-10-02",
"average": 2.6038
}
}A range that reaches a date NBG has not published yet is an error, as is a day in the range for which NBG's export has no rate of the currency (for example before NBG first quoted it, or after NBG stopped).
Resource nbg://rates/{date}
The full official NBG rate table in force on a calendar date (YYYY-MM-DD) or on today, as JSON with the same fields as the nbg_get_rates output and English currency names. nbg://rates/today is listed among the server's resources.
Errors
A tool that cannot answer returns an error result whose text says what went wrong and what to do next:
the date is not a real calendar date in the form
YYYY-MM-DD, or a history range ends before it starts;a history range is longer than 366 days;
the currency is not a three-letter code (the message names USD, EUR and GEL as examples), or NBG did not quote it on the requested date or range (
nbg_get_rateslists such codes inunknownCodesinstead); NBG has added and dropped currencies over the years, so the code may still have rates on other dates;a conversion result is too large to represent as a number;
NBG has no rate in force on the date (the archive starts on 1995-10-14, and a currency has no rate before NBG first quoted it, after NBG stopped quoting it, or where NBG's records have a gap);
NBG has not published a rate for the date yet; the message names today in Tbilisi, says that tomorrow is the furthest date that can have a rate and only once NBG has published it, and, when known, names the date from which the latest published rate is valid. A history range that reaches such a date fails as a whole: end it at today;
NBG did not respond usably; the request can be retried;
the NBG response did not have the expected shape, which means the NBG endpoint may have changed; please open an issue with the package version.
Reading the resource fails with the same messages.
Date rules
NBG sets a rate on business days around 17:00 Tbilisi time, valid from the next calendar day until the next rate. Once it is published, tomorrow's rate can be requested by date.
"Today" means the calendar date in Tbilisi, wherever the client runs.
Sundays, Mondays and days after a public holiday have no rate of their own: they carry the earlier rate with
carriedOver: trueandeffectiveDateset to the day it took effect. Before September 2021 NBG stored a rate for every calendar day, so older Sundays are not carried over.A date whose rate NBG has not published yet (tomorrow before about 17:00 Tbilisi time, or any later date) returns an error, never a guess.
NBG occasionally publishes a table for a day after that day: on Monday 2026-09-28 at 17:01 it published the table valid from Saturday 2026-09-26, which replaced Friday's rate for 2026-09-26 to 2026-09-28. A carried-over answer for the last seven days can therefore change; the server asks NBG again after ten minutes for those, and after twelve hours for every other answer.
The archive starts on 1995-10-14 with USD; other currencies start later.
Environment variables
Variable | Effect |
| The NBG host to call. Defaults to |
| Set to |
| Host names the HTTP endpoint ( |
| The token OpenAI's domain verification expects at |
Set them in the client's configuration, for example:
{
"mcpServers": {
"nbg-rates": { "command": "npx", "args": ["-y", "nbg-rates-mcp"], "env": { "NBG_RATES_DEBUG": "1" } }
}
}Development
npm install
npm test # unit tests
npm run test:e2e # builds, then drives the server over stdio against recorded NBG responses
npm run test:contract # live: checks the assumptions against nbg.gov.ge
npm run record-fixtures # refreshes test/fixtures from nbg.gov.ge; review the diff before committingBefore a pull request, also run npm run format:check, npm run lint and npm run typecheck.
The code is split into src/core, pure functions with no I/O (date rules, parsing, per-unit normalisation, conversion, history), and src/shell, which holds the effects (the NBG HTTP client, the cache, the rates service and the MCP server). src/bin.ts is the executable.
The repository's rulesets (protection of main, and release tags that only admins may create) are defined in .github/rulesets/ and applied with scripts/apply-rulesets.sh; its description, homepage and topics are defined in .github/repository.json and applied with scripts/apply-repo-settings.sh. Both are run by a maintainer after a change, never edited in the GitHub UI.
License
MIT, see LICENSE.
Available Tools
5 toolsnbg_convertConvert via NBG rateARead-onlyIdempotentInspect
Converts an amount between two currencies using the official NBG rate in force on a calendar date. Either side may be GEL; a pair without GEL is converted through GEL (via: "GEL"). Show resultRounded as the amount; result is the unrounded value for further calculation. Dates are calendar days in Tbilisi; the default is today. NBG sets a rate on business days around 17:00 Tbilisi time, valid from the next calendar day until the next rate, so after 17:00 tomorrow can be requested by date. Sundays, Mondays and days after a public holiday have no rate of their own: the rate in force was set earlier and is returned with carriedOver true and effectiveDate set to the day it took effect. Quote effectiveDate when carriedOver is true. A date whose rate NBG has not published yet returns an error, never a guess.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | ISO 4217 code, or GEL. | |
| date | No | Calendar date YYYY-MM-DD whose official rate to apply. Defaults to today in Tbilisi. | |
| from | Yes | ISO 4217 code, or GEL. | |
| amount | Yes | Amount in the "from" currency. May be negative. |
Output Schema
| Name | Required | Description |
|---|---|---|
| to | Yes | |
| via | Yes | |
| from | Yes | |
| rate | Yes | One unit of "from" expressed in "to" |
| amount | Yes | |
| result | Yes | Unrounded |
| carriedOver | Yes | true when the rate in force on requestedDate took effect on an earlier day; it is still the official rate |
| effectiveDate | Yes | Calendar date the returned rate took effect, as published by NBG |
| requestedDate | Yes | |
| resultRounded | Yes | result rounded half up to 2 decimals from the exact value; the amount to show. JSON drops trailing zeros, so 3.90 arrives as 3.9. Exact up to about 7e13 in the target currency; above that the nearest double. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, non-destructive), and the description adds substantial behavior beyond them: rate publication at ~17:00 Tbilisi with next-day validity, carried-over rates on Sundays/Mondays/post-holidays flagged via carriedOver and effectiveDate, and an explicit 'error, never a guess' failure mode. That is exactly the extra context an agent needs to interpret results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One dense paragraph, front-loaded with the core purpose, then usage, then output/date semantics. Every sentence carries information, though the 17:00/carried-over timing block is lengthy and could be tightened without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter conversion tool with annotations and an output schema, the description covers input constraints, the GEL routing rule, output-field usage (resultRounded vs result), date/rollover behavior, and error semantics. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it constrains 'from'/'to' to currency sides (one may be GEL), explains the implicit GEL conversion and the 'via' output value, and clarifies 'date' is a Tbilisi calendar day defaulting to today with post-17:00 forward availability.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb and resource ('Converts an amount between two currencies') and pins the rate source and effective-date semantics. This clearly separates it from nbg_convert_many (bulk) and nbg_rate_history (historical listing), so an agent can route correctly without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete usage context: either side may be GEL, non-GEL pairs route through GEL with a 'via' value, and the date defaults to today in Tbilisi. It also implies the single-pair use case but never explicitly names an alternative tool (e.g. nbg_convert_many) or states exclusions, so it stops short of a full when/when-not statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
nbg_convert_manyConvert a list via NBG ratesARead-onlyIdempotentInspect
Converts a list of amounts, each at the official NBG rate in force on its own date: one call for a list of invoices. Items are answered independently; a bad date or code fails only its item, with the error text in that item. total is present when every converted item has the same target currency and covers the converted items only; call it partial when skipped is not 0. Once NBG stops answering, the remaining dates fail with the same error without being asked. Show resultRounded as the amount; result is the unrounded value for further calculation. Dates are calendar days in Tbilisi; the default is today. NBG sets a rate on business days around 17:00 Tbilisi time, valid from the next calendar day until the next rate, so after 17:00 tomorrow can be requested by date. Sundays, Mondays and days after a public holiday have no rate of their own: the rate in force was set earlier and is returned with carriedOver true and effectiveDate set to the day it took effect. Quote effectiveDate when carriedOver is true. A date whose rate NBG has not published yet returns an error, never a guess.
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes | 1 to 100 conversions, each with the fields of nbg_convert; items are answered independently. |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | Yes | One entry per input item, in input order: a conversion, or the item with an error |
| total | No | Present only when at least one item converted, every converted item has the same target currency, and the sum is a finite number. It covers the converted items only: when skipped is not 0, say the total is partial and list the skipped items. |
| skipped | Yes | |
| converted | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already cover read-only, open-world, idempotent, and non-destructive behavior, but the description adds substantial operational context: per-item error isolation, when total becomes partial via skipped, NBG outage fallback, resultRounded vs result semantics, Tbilisi calendar rules, rate publication timing, and carriedOver/effectiveDate behavior. It also states that unpublished rates return an error rather than a guess.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Despite its length, the description is front-loaded with purpose and then proceeds through item independence, aggregation, rate timing, and output-field guidance. Each sentence carries necessary behavioral detail for a complex batch-rate tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, but the description still explains crucial return-field semantics like resultRounded, result, total, skipped, carriedOver, and effectiveDate. Combined with detailed failure and rate-publication rules, an agent has enough context to call and interpret this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the nested item fields are already documented with descriptions and defaults. The description reinforces date semantics ('calendar days in Tbilisi', default today) but adds little beyond what the schema already states for the items parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Converts), the resource (a list of amounts), and the key scoping rule (each at the official NBG rate in force on its own date). The phrase 'one call for a list of invoices' makes the batch purpose distinct from the single-conversion sibling nbg_convert.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for use: converting a list of invoices in one call, with items answered independently. It does not explicitly name when to use nbg_convert instead or state exclusions, so it falls short of full alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
nbg_get_ratesNBG rates for a dateARead-onlyIdempotentInspect
Official National Bank of Georgia (NBG) exchange rates of the Georgian lari (GEL) in force on one calendar date. Every rate is GEL per ONE unit of the currency (NBG quotes some currencies per 10, 100, 1000 or 10000 units; the raw pair is returned as nbgQuantity and nbgRate). Dates are calendar days in Tbilisi; the default is today. NBG sets a rate on business days around 17:00 Tbilisi time, valid from the next calendar day until the next rate, so after 17:00 tomorrow can be requested by date. Sundays, Mondays and days after a public holiday have no rate of their own: the rate in force was set earlier and is returned with carriedOver true and effectiveDate set to the day it took effect. Quote effectiveDate when carriedOver is true. A date whose rate NBG has not published yet returns an error, never a guess. When nbgQuantity is not 1, state NBG's quoted form first, nbgRate GEL per nbgQuantity units (for example 1.6452 GEL per 100 JPY), and the per-unit rate beside it: that quoted form is what nbg.gov.ge and accounting software show.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | Calendar date YYYY-MM-DD. Defaults to today in Tbilisi. Tomorrow's rate exists after about 17:00 Tbilisi time. | |
| language | No | Language of currency names: en (default) or ka (Georgian). Codes, numbers and dates are identical in both. | |
| currencies | No | ISO 4217 codes to return, case-insensitive, duplicates ignored (for example ["USD", "EUR"]). Omit for all currencies. GEL has no row, since every rate is in GEL, so it is listed in unknownCodes. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rates | Yes | |
| carriedOver | Yes | true when the rate in force on requestedDate took effect on an earlier day; it is still the official rate |
| unknownCodes | Yes | |
| effectiveDate | Yes | Calendar date the returned rate took effect, as published by NBG |
| requestedDate | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld, and the description layers substantial extra behavior on top: carriedOver/effectiveDate semantics, the guarantee that unpublished dates return an error 'never a guess,' and the nbgQuantity quoting convention. This is real context the agent cannot get from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and nearly every sentence carries operational weight (timing, carried-over handling, error guarantee). It runs long at roughly 200 words, and the final output-formatting instruction partly duplicates what the output schema covers, but there is little true filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a rate-lookup tool with an output schema, it covers everything an agent needs: default date, business-day publication timing, carried-over behavior, error-on-unpublished, and how to present multi-unit quotes. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so date, language and currencies are already fully documented, including the 17:00 timing note that the description repeats. The extra detail about nbgQuantity and GEL landing in unknownCodes concerns output interpretation rather than the input parameters, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: official NBG GEL exchange rates 'in force on one calendar date.' The single-date framing implicitly separates it from nbg_rate_history, but no sibling is named explicitly, so it stops short of full sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Offers operational timing guidance (default is today; after 17:00 Tbilisi tomorrow can be requested), which implies when the tool applies. However, it never says when to prefer this over nbg_convert, nbg_convert_many or nbg_rate_history, leaving tool selection to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
nbg_list_currenciesNBG currency listARead-onlyIdempotentInspect
Lists every currency NBG publishes a GEL rate for today, with its name and the unit quantity NBG quotes it in. NBG quoted other currencies in the past, so a code missing here may still have historical rates. nbgQuantity is the unit count NBG quotes the raw rate for (JPY is quoted as 1.6452 GEL per 100 JPY); for the rates themselves call nbg_get_rates.
| Name | Required | Description | Default |
|---|---|---|---|
| language | No | Language of currency names: en (default) or ka (Georgian). Codes, numbers and dates are identical in both. |
Output Schema
| Name | Required | Description |
|---|---|---|
| currencies | Yes | |
| effectiveDate | Yes | Calendar date the returned rate took effect, as published by NBG |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so the safety profile is covered. The description adds real behavioral context beyond that: the list is a today-only snapshot, absent codes may still have history, and nbgQuantity explains the quoting unit (with a concrete JPY example) that would otherwise be misinterpreted as a raw price.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each carrying distinct load: scope, historical caveat, and unit semantics plus alternative routing. The scoping statement is front-loaded and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no prose explanation, and the annotations cover the safety profile. What remains — the today-only scope, the historical-gap caveat, and the quoting-unit semantics — is exactly what the description supplies.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single language parameter is fully documented in the schema, including the en/ka enum and the default. The description adds nothing about the parameter, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Lists every currency NBG publishes a GEL rate for today') and scopes it to today's published set. It explicitly routes rate retrieval to the sibling nbg_get_rates, so an agent can distinguish it from nbg_get_rates without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: 'for the rates themselves call nbg_get_rates', which names the alternative and the condition that selects it. It also warns that a missing code may still have historical rates, preventing a false negative when the agent is deciding between this tool and nbg_rate_history.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
nbg_rate_historyNBG rate historyARead-onlyIdempotentInspect
Official NBG rate of one currency (GEL per one unit) in force on every calendar day of an inclusive range of at most 366 days. Dates are calendar days in Tbilisi. Days without a rate of their own (Sundays, Mondays and days after a public holiday; before September 2021 NBG set a rate for every calendar day) carry the earlier rate. Days whose own rate is in force contain only date and rate; a carried-over day adds effectiveDate and carriedOver: true. Quote effectiveDate when carriedOver is true. A range reaching a date NBG has not published yet returns an error. summary holds the first, last, change, lowest and highest rate with their dates, and the average over the calendar days, computed by the server: report those rather than deriving them. One NBG request per call.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | Last calendar day, inclusive. At most 366 days including "from". | |
| from | Yes | First calendar day, inclusive. | |
| currency | Yes | ISO 4217 code, for example USD. |
Output Schema
| Name | Required | Description |
|---|---|---|
| to | Yes | |
| days | Yes | |
| from | Yes | |
| summary | Yes | Computed by the server; report these instead of deriving them from days. change and average are exact, rounded half up to the decimals NBG's quantity gives the currency, 4 + log10(nbgQuantity), the largest over the days (4 for USD, 7 for AMD). |
| currency | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the safety profile (readOnly, idempotent, non-destructive, open world); the description goes well beyond them by disclosing carry-over semantics, the effectiveDate/carriedOver fields and when to quote them, the error on unpublished dates, the Tbilisi calendar basis, and the one-request-per-call limit. This is exactly the behavioral context an agent needs to interpret results correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and scope are front-loaded in sentence one, and every sentence carries information (carry-over, errors, summary, request budget). The middle section is a dense run-on that takes effort to parse, but there is no filler to cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter, single-request range query with an output schema, the description covers the edge cases that matter: carry-over days, unpublished-date errors, and the server-computed summary. Even though an output schema exists, the explanation of carriedOver semantics is the key to correct interpretation and is fully provided.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds genuine meaning beyond the schema by fixing the unit (GEL per one unit) and the timezone basis (Tbilisi calendar days), neither of which appears in the parameter descriptions. It restates the 366-day cap rather than adding to it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a specific resource (official NBG rate), its unit basis (GEL per one unit), and its exact scope (every calendar day of an inclusive range up to 366 days). That scope is clearly distinguishable from the convert and list_currencies siblings, which serve different jobs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the range-based framing, but no sibling is named and there is no explicit statement of when to prefer this over nbg_get_rates or nbg_convert. The closest thing to guidance is operational ('one NBG request per call', 'report summary rather than deriving them'), which governs use of the result rather than tool selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
nbg_convert - First observed
nbg_convert_many - First observed
nbg_get_rates - First observed
nbg_list_currencies - First observed
nbg_rate_history
TDQS
Scored across 5 tools
Each tool has a clearly distinct purpose: single-date multi-currency rates (nbg_get_rates) vs single-currency date range (nbg_rate_history) vs single conversion (nbg_convert) vs batch conversion (nbg_convert_many) vs currency listing (nbg_list_currencies). The single/batch and single-date/range distinctions are spelled out in the descriptions, leaving little room for misselection.
All names share a consistent nbg_ prefix and snake_case, and four of five are verb-first (get_rates, convert, convert_many, list_currencies). nbg_rate_history breaks the verb-first convention by leading with a noun, a minor deviation from an otherwise predictable pattern.
Five tools is well-scoped for an exchange-rate service, covering current rates, historical ranges, single conversion, batch conversion, and currency enumeration. Each tool earns its place with no redundant or filler operations.
The surface covers the full lifecycle of the domain: discovering available currencies, fetching rates for a date or a range, and converting both single and batch amounts. No obvious gaps or dead ends for rate lookup and conversion workflows.
Maintenance
Related MCP Connectors
ExchangeRate MCP — wraps open.er-api.com (free, no auth)
Convert dated amounts to a supported target currency with traceable ECB reference rates, or retrieve recent and historical EUR reference exchange rates since 1999. Exact source dates, original decimals, citations and explicit gaps. New practical task: convert_currency_batch accepts rows with a unique id, date, currency and decimal-string amount. It preserves original amounts, source rate decimals and evidence, applies an explicit rounding rule and returns a line-by-line result. Missing, stale or mismatched rates block the complete CSV and total. A weekend date is never silently replaced by a previous working day. Try: convert 125.50 USD dated 2025-09-26 to EUR and explain the rate and rounding. Up to 500 rows, 48,000 UTF-8 argument bytes and eight currency/year snapshots per call. Start small. JSON is the default; output_format=both adds CSV in the same free result. EUR rows use identity, not an external rate. DatoQA processes supplied rows in memory without retaining their content; your client or gateway may keep its own logs. Existing tools still provide exact-date/year history and latest references. Source availability varies by currency and year; no forward filling or interpolation. Original ECB data is free. References and derived conversions are indicative, not live executable prices, transfers or accounting/tax certification. Source/schema checks are not independent factual verification. 7 focused MCP tools. Free during the pilot; no account, API key or wallet required. Same DatoQA source snapshots as the general server; public pilot without an SLA. Setup, examples and limits: https://datoqa.bhazarstudio.workers.dev/docs/fx/ Datoka portal and connection guide: https://datoka.dev/qa.
Exchange MCP — wraps the Frankfurter currency exchange API (free, no auth)
Official ECB euro reference rates for invoices and tax returns. $0.004/call via x402.
Related MCP Servers
- FlicenseBqualityDmaintenanceAn MCP server that provides currency rates, conversions, and historical exchange-rate data using the Frankfurter API. It enables users to retrieve latest rates, convert amounts between currencies, and access time-series data for currency pairs.3-
- -licenseNot gradedqualityNot gradedmaintenanceMCP server that provides real exchange-rate data from the European Central Bank, including latest rates, currency conversion, historical rates, and time series.-
- AlicenseAqualityBmaintenanceLocal MCP server for the Polish National Bank (NBP) public API enabling exchange rates, gold prices, and currency conversion without authentication.6MIT
- AlicenseNot gradedqualityAmaintenanceMCP server for the Frankfurter exchange-rate API, enabling currency conversion and rate queries.29 npm4MIT