ifsc-mcp
Wraps the razorpay.com IFSC API and the razorpay/ifsc dataset to provide Indian bank branch data. Tools include single-IFSC branch lookup (returning address, MICR, SWIFT, contact and IMPS/NEFT/RTGS/UPI support), offline IFSC format validation, branch search by bank code/city/state/branch, discovery of valid state/district/branch filter spellings, a bank name to 4-letter code directory, and bank metadata such as bank type and NACH/ACH/APBS/UPI rail support.
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., "@ifsc-mcplook up IFSC HDFC0001234 and tell me if it supports UPI and NEFT"
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.
ifsc-mcp
An MCP server that gives an LLM accurate Indian bank branch data: look up a branch by IFSC, validate a user-typed IFSC offline, find branches by bank/city/state, and map bank names to their 4-letter codes.
The problem
Every Indian payment integration needs IFSC data, and every team solves it badly. The usual options are a stale CSV someone committed in 2021, a paid API with a card on file, or scraping a bank website. Meanwhile the LLM in front of it hallucinates: it invents IFSC codes, guesses that a bank supports UPI, and returns branch addresses that were renamed years ago. IFSCs are 11 characters and unforgiving — one wrong character means a payment sits in a NEAF account and nobody knows why.
This server wraps the razorpay.com IFSC API and the published razorpay/ifsc dataset, so the model asks a tool instead of guessing. Invalid input is rejected locally before a request is made, and every answer carries the branch's real address, MICR and settlement-rail support.
Related MCP server: Moon Banking MCP Server
Install
Not on PyPI yet — install from source or run straight from the repo:
git clone https://github.com/uttkarsh-26/ifsc-mcp.git
cd ifsc-mcp
uv venv .venv && uv pip install -e ".[dev]"Or without cloning, into any Python environment:
pip install "git+https://github.com/uttkarsh-26/ifsc-mcp"MCP client config
Ready to paste into Claude Desktop, Cursor, VS Code or any MCP client:
{
"mcpServers": {
"ifsc": {
"command": "ifsc-mcp",
"transport": "stdio"
}
}
}Via uvx, no install needed:
{
"mcpServers": {
"ifsc": {
"command": "uvx",
"args": ["--from", "git+https://github.com/uttkarsh-26/ifsc-mcp", "ifsc-mcp"]
}
}
}Streamable HTTP instead of stdio:
ifsc-mcp --transport http --host 127.0.0.1 --port 8000 --path /mcp{
"mcpServers": {
"ifsc": { "type": "http", "url": "http://127.0.0.1:8000/mcp" }
}
}Tools
Tool | Purpose | Key parameters | Notes |
| Full details for one branch |
| The only tool that confirms a branch exists. Returns address, MICR, SWIFT, contact, |
| Offline format check |
| No network. 4 letters + |
| Find branches by bank/city/state/branch |
| Filters are ANDed; at least one is required. Paged, with |
| Discover valid filter spellings |
| Walks states → districts → branches. Use when a search returns nothing. |
| Bank name → 4-letter code |
| 1511+ banks, cached. Returns |
| Bank type and rail support |
| Private/public/foreign/SFB/payments/co-op, plus UPI, ACH, NACH, APBS flags. |
Every result carries ok: true. Every failure returns ok: false with a stable code:
INVALID_INPUT, NOT_FOUND, UPSTREAM_TIMEOUT, UPSTREAM_UNAVAILABLE,
UPSTREAM_ERROR, BAD_UPSTREAM_RESPONSE, UNKNOWN — never a traceback.
Configuration
All optional, all read from the environment:
Variable | Default | Purpose |
|
| Upstream API host. |
|
| Bank dataset host. |
|
| Read timeout, seconds. |
|
| Connect timeout, seconds. |
|
| Retries on timeout/429/5xx. |
|
| Bank-directory cache TTL, seconds. |
|
|
|
|
|
|
|
| HTTP binding. |
Data sources
Source | Used for | Verified |
| Single-IFSC lookup | 200 + JSON |
| Branch search by bankcode/city/state/branch | 200 + JSON, 400 on bad limit |
| State/district/branch name discovery | 200 + JSON, 400 with no bankcode |
| Bank code → name (1511 entries) | 200 + JSON |
| Bank type, UPI/NACH/ACH, MICR, IIN | 200 + JSON |
Evals
The repo scores its own tool descriptions. evals/golden.json holds 32
natural-language queries with the expected tool and expected arguments, spread
across all six tools. Two scorers run against it:
router— a deterministic, LLM-free keyword router. No network, no API key, same answer every run. This is the number that matters: it measures whether the descriptions make the tool boundaries legible.live— an optional model runner using any OpenAI-compatible endpoint fromOPENAI_API_KEY/OPENAI_BASE_URL. Skipped cleanly when unset.
# deterministic baseline, no network and no API key
.venv/bin/python -m evals.run --scorer router
# optional model-backed run
OPENAI_API_KEY=... OPENAI_BASE_URL=... .venv/bin/python -m evals.run --scorer liveMeasured, on the committed golden set:
Scorer | Model | Passed | Total | Pass rate | Threshold | Verdict |
| none — keyword rules | 32 | 32 | 100.0% | 85% | PASS |
|
| 31–32 | 32 | 96.9–100% | 90% | PASS |
Read those numbers honestly. The 32-case golden set is a smoke gate, not a
benchmark, and the keyword baseline was tuned against this same set — it is a
floor for "are the tool boundaries legible at all", not a generalisation claim.
The live figure is the interesting one, and it is not stable: two consecutive
runs scored 32/32 and 31/32. The one flaky case (search-05) is a real finding,
not noise — given "find the Axis Bank branch named PALAKKAD KERALA", the model
sometimes drops bank_code because the bank name is buried inside the branch
name. Fixing that is on the roadmap below.
Results are written to evals/results/results.json (machine-readable) and
evals/results/results.md (human table, including the per-case routing
confusion list). The runner exits non-zero below the threshold in
evals/config.json, so a regression fails CI instead of quietly being a number
nobody reads.
See EVALS.md for the scoring rules, the per-tool breakdown, the routing-confusion list and what the eval caught in the tool design.
Verification
Everything in this README was executed, not assumed.
.venv/bin/pytest # 131 unit tests, hermetic and offline
.venv/bin/pytest -m live # 6 live upstream smoke tests
.venv/bin/python scripts/verify_e2e.py # real MCP client over stdio -> VERIFICATION.md
.venv/bin/python scripts/verify_http.py # real MCP client over streamable HTTPVERIFICATION.md holds a raw transcript: a real
mcp.Client subprocess session performing a genuine handshake, tools/list and
nine tools/call round trips against the live API.
Development
uv pip install -e ".[dev]"
.venv/bin/ruff check . && .venv/bin/ruff format --check .
.venv/bin/pytest # unit tests (live ones deselected)
.venv/bin/pytest -m live # real network smoke testsAfter editing the golden cases in evals/golden.py, regenerate the JSON that
the runner and the tests read:
.venv/bin/python -c "
import json
from evals.golden import _CASES, MIN_CASES
with open('evals/golden.json','w') as f:
json.dump({'version':1,'min_cases':MIN_CASES,'cases':list(_CASES)}, f, indent=2); f.write('\n')
"Roadmap
Make
search_branchesinferbank_codefrom the branch string (or allow it to be omitted whenbranchis present) — the one case the live eval misses.Bundle a pinned snapshot of the bank dataset so
bank_directoryworks offline.A
nefc_advicetool: given account number + IFSC, compute the NEFC limit and thea/anda@sublet categories RBI assigns.Search by pincode, which RBI publishes but the current API does not expose.
Prompt caching for
bank_directoryoutput to cut repeat token cost.
License
MIT — see LICENSE.
Upstream data is provided by razorpay/ifsc (MIT) and the razorpay.com IFSC API. This project is not affiliated with Razorpay or the RBI.
Available Tools
6 toolsbank_directoryList or search Indian bank codes and namesARead-onlyIdempotent
Look up the official 4-letter bank code for a bank, or list banks by name.
Every IFSC begins with a 4-letter bank code, and this is the tool that maps
"HDFC Bank" -> "HDFC". Use it before ifsc_lookup or search_branches when
the user gave a bank name rather than a code.
Returns ok: true with: banks (list of {"code", "name"}), count (total
matches) and source (the upstream dataset URL).
Data comes from the published razorpay/ifsc dataset and is cached, so
repeated calls are cheap. This returns banks, not branches - use
search_branches or ifsc_lookup for a specific branch.
Args: query: Substring filter over code and name; omit for the full list. limit: Max rows, 1-500 (default 50, or 500 with no query).
Returns: Matching bank codes and names, or a structured error payload.
Example: Ask "what is the code for Kotak Mahindra?" -> call bank_directory(query="kotak") and read banks[0]["code"].
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max banks to return, 1-500. Defaults to 50, or 500 with no query. | |
| query | No | Optional case-insensitive fragment matched against both the 4-letter code and the full bank name, e.g. 'hdfc', 'kotak', 'state bank', 'cooperative'. Omit to list every bank (1511+). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent and open-world behavior, so the safety profile is covered. The description adds real context beyond that: the upstream razorpay/ifsc dataset, caching that makes repeated calls cheap, and the shape of the success and error payloads. It stops short of richer disclosure such as refresh cadence or staleness of the cached data.
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?
Opening sentence is front-loaded and the routing guidance comes early, which is the right priority order. There is mild redundancy: return values are described twice (a detailed 'Returns ok: true with...' block and a second 'Returns:' line), which costs a little tightness.
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?
With an output schema present the description need not enumerate return fields, yet it still covers routing, data source, caching, error shape, and a worked example. Nothing an agent needs to select or invoke this 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 description coverage is 100%, so both parameters are already fully documented in the schema, which sets the baseline at 3. The description's Args section largely restates the schema (substring filter, omit for full list, 1-500 range) and adds little beyond the default-scaling note already present in the schema.
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 ('look up the official 4-letter bank code ... or list banks by name') and immediately scopes what it is not ('this returns banks, not branches'). The HDFC -> HDFC mapping example makes the transform concrete and distinguishable from branch-oriented siblings.
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?
Explicitly states when to use it ('before ifsc_lookup or search_branches when the user gave a bank name rather than a code') and names the alternatives for the opposite case ('use search_branches or ifsc_lookup for a specific branch'). Both the selection condition and the exclusions are spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bank_metadataGet bank type and UPI/NACH/ACH support for a bank codeARead-onlyIdempotent
Get bank-level facts for a 4-letter bank code: its type, MICR/IIN, and payment-rail support.
Distinguishes bank classes that share an IFSC prefix pattern - private / public / foreign / small finance / payments / co-operative - and reports whether the bank as a whole supports UPI, ACH credit/debit, NACH debit and APBS.
Choose this over bank_directory when you need capabilities or bank type
rather than the name; choose bank_directory when you need to go from a bank
name to its code.
Returns ok: true with: bank_code, name, bank_type, micr, iin,
capability_ifsc, and capabilities (upi, ach_credit, ach_debit, nach_debit, apbs).
Args: bank_code: The 4-letter bank code, e.g. "KKBK".
Returns: Bank metadata, or a structured error payload (INVALID_INPUT for a malformed code, NOT_FOUND for a code absent from the dataset).
Example: Ask "is HDFC a private bank and does it support UPI?" -> call bank_metadata(bank_code="HDFC").
| Name | Required | Description | Default |
|---|---|---|---|
| bank_code | Yes | A 4-letter bank code as found in an IFSC or via bank_directory, e.g. 'HDFC', 'SBIN', 'KKBK'. Uppercased for you. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnly, idempotent, openWorld), and the description adds real context beyond them: the key semantic that capabilities are reported for the *bank as a whole* rather than a branch, plus the specific failure modes (INVALID_INPUT for malformed code, NOT_FOUND for absent code). It stops short of noting dataset staleness or any rate limits.
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?
Front-loaded with the core purpose and routing advice where they matter most. However, the return fields are listed twice (inline in the body and again under an explicit 'Returns:' block), and the Args/Returns/Example scaffolding duplicates information already given.
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 one-parameter read tool with an output schema and annotations, the description covers scope (bank-level, not branch-level), routing to the sibling, expected success payload, and error payloads. Nothing an agent needs in order to select or invoke 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 description coverage is 100% and the sole parameter is fully documented in the schema, including length constraints and the uppercase behavior, so the baseline is 3. The description's `bank_code` note and "KKBK" example merely restate what the schema already says.
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 ('Get bank-level facts for a 4-letter bank code') and enumerates the exact payload (type, MICR/IIN, payment-rail support). It explicitly differentiates itself from the sibling `bank_directory` by scope (capabilities/type vs name lookup).
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 a direct routing rule in both directions: use this tool for capabilities or bank type, use `bank_directory` to go from a bank name to its code. That is an explicit when/when-not with a named alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
branch_placesList valid states, districts and branch names for a bankARead-onlyIdempotent
Discover the exact district and branch-name spellings that search_branches matches on.
The branch search API matches city/district and branch verbatim, so a
near-miss returns zero rows. This tool walks the same hierarchy the API uses,
one level per call, and returns the accepted values at that level:
bank_code alone -> states; add state -> districts; add district ->
branches.
Returns ok: true with level ('states'|'districts'|'branches'), the values
at that level, total found, truncated, and the echoed query.
Call this when a search returned nothing and you need to check spelling - it is cheaper than guessing several more search calls.
Args: bank_code: 4-letter bank code; required. state: ISO 3166-2 code to descend to districts, e.g. "IN-MH". district: District name to descend to branches, e.g. "MUMBAI". limit: Cap on returned values, 1-200 (default 200).
Returns: The place names valid at the requested level, or a structured error payload.
Example: Ask "which states does Kotak serve?" -> call branch_places(bank_code="KKBK").
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Cap on how many values to return, 1-200. Defaults to 200. | |
| state | No | Pass an ISO 3166-2 code ('IN-MH') to move from the states level to the districts level. Omit it to get the list of states. | |
| district | No | Pass a district name from the previous level (e.g. 'MUMBAI') to get the branch names inside it. Requires `state`. | |
| bank_code | Yes | Required 4-letter bank code, e.g. 'HDFC'. The listed values are specific to this bank - a district HDFC operates in may not appear for another bank. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld, so the safety profile is covered; the description adds the level-by-level traversal behavior (`bank_code` -> states, +`state` -> districts, +`district` -> branches) and notes truncation. This is meaningful added context beyond the annotations, though it is somewhat redundant with the separately documented output schema.
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?
Front-loads the purpose and the traversal model before args/returns, and each sentence carries weight. The Returns section duplicates what the output schema already provides, so it is slightly longer than strictly necessary.
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 further explanation here. Between the description, schema, and annotations, an agent has everything needed to select and invoke 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 description coverage is 100%, so every parameter is already documented, including the hierarchy semantics and the 'requires state' constraint. The description's Args section largely restates the same example values, adding little beyond the schema baseline.
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 description states a specific verb and resource ('Discover the exact district and branch-name spellings') and explicitly names the sibling it complements ('search_branches'), so an agent can distinguish it without opening schemas.
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 an explicit trigger condition ('Call this when a search returned nothing and you need to check spelling') and a rationale for preferring it over the alternative ('cheaper than guessing several more search calls'), effectively naming search_branches as the alternative path.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ifsc_lookupLook up one bank branch by IFSCARead-onlyIdempotent
Retrieve full details for the single bank branch identified by an IFSC code.
Use this whenever the user supplies an IFSC. It is the only tool that confirms a branch actually exists, and the only source of the branch's address, MICR, SWIFT/bic code, contact number and per-branch settlement-rail support (RTGS, NEFT, IMPS, UPI).
Returns ok: true with: ifsc, bank, bank_code, branch, centre, address,
city, district, state, iso3166, micr, contact, swift, and supports
(booleans for imps/neft/rtgs/upi/swift).
Failure codes: INVALID_INPUT (not 11 chars, or not 4 letters + '0' + 6 alphanumerics), NOT_FOUND (well-formed but no such branch), UPSTREAM_TIMEOUT / UPSTREAM_UNAVAILABLE / UPSTREAM_ERROR (service trouble).
Args: ifsc: The 11-character IFSC to resolve, e.g. "SBIN0000001".
Returns:
The branch record, or a structured error payload with ok: false.
Example: Ask "what is the MICR of HDFC0000001?" -> call ifsc_lookup("HDFC0000001").
| Name | Required | Description | Default |
|---|---|---|---|
| ifsc | Yes | The 11-character IFSC code of the branch, e.g. 'HDFC0000001'. Exactly 4 letters (bank code) + '0' + 6 alphanumeric characters. Case-insensitive; surrounding whitespace is trimmed. Must be a real, specific branch code - not a bank code like 'HDFC'. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/openWorldHint, so the safety profile is covered. The description adds genuinely useful behavior beyond that: the exact failure taxonomy (INVALID_INPUT, NOT_FOUND, three UPSTREAM_* variants) and the ok:true/ok:false shape. No rate limits or latency expectations, but this is strong added context.
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?
Front-loaded with purpose and usage, then well-sectioned failure codes and an example that do real work. The 'Args:' and 'Returns:' blocks largely duplicate the input schema and the output schema, which is mild waste but far from bloat.
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 single-lookup tool with 100% schema coverage and an output schema already present, the description covers purpose, trigger, error taxonomy and return-field inventory. 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 description coverage is 100% and the single parameter is fully documented there (format, case-insensitivity, whitespace trimming, not-a-bank-code). The description restates the same 11-character rule and example, adding no meaning beyond the schema, so the 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 (retrieve) and resource (single bank branch) scoped by an identifier, and explicitly carves out uniqueness: 'the only tool that confirms a branch actually exists, and the only source of the branch's address, MICR, SWIFT/bic code...'. That contrast with ifsc_validate and the directory-style siblings is enough for an agent to 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?
'Use this whenever the user supplies an IFSC' gives a clear triggering condition. It does not name ifsc_validate or search_branches as the alternative or state when NOT to use this tool, so it falls short of explicit alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ifsc_validateValidate IFSC format offlineARead-onlyIdempotent
Check whether a string is structurally a valid IFSC, without any network call.
Applies the RBI/NEFC layout rules offline: exactly 11 characters, 4 leading letters for the bank code, a literal '0' as the 5th character, and 6 trailing alphanumeric branch characters.
Choose this over ifsc_lookup when you want to reject junk before spending a
request, or to explain why a user-entered code is wrong. It does NOT confirm
the branch exists - only ifsc_lookup does that.
Returns ok: true with: input, normalized, is_valid, bank_code, branch_code,
reason and message. reason is a stable code: VALID, EMPTY, WRONG_LENGTH,
INVALID_CHARACTERS, BAD_BANK_CODE, MISSING_BANK_SUBLET or BAD_BRANCH_CODE.
Args: ifsc: The candidate string to validate.
Returns:
A validation report, always with ok: true - an invalid IFSC is a
successful report, not an error.
Example:
Ask "is HFDCC00001 a valid IFSC?" -> call ifsc_validate("HFDCC00001") and
read reason == "MISSING_BANK_SUBLET".
| Name | Required | Description | Default |
|---|---|---|---|
| ifsc | Yes | The candidate IFSC string to check, e.g. 'HDFC0000001' or a messy user-typed value like ' hdfc 0000001 '. Case and whitespace are normalised before checking. May be empty - an empty value is a reportable result (reason EMPTY), not an error. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/closed-world, but the description adds genuinely useful behavior beyond them: no network call, normalization of case/whitespace, the always-`ok: true` contract ('an invalid IFSC is a successful report, not an error'), and the enumerated stable `reason` codes. That is real operational context an agent cannot get from the annotations alone.
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?
Front-loaded with the core purpose and the sibling tradeoff, then laid out in readable blocks. The Returns section partially restates what the declared output schema already provides, which is mild redundancy, but the Example is compact and instructive.
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 single-parameter, offline, idempotent validator, everything an agent needs is present: the layout rules, normalization behavior, empty-input handling, the success-vs-error contract, and the reason codes. An output schema exists, so return-value detail is a bonus rather than a gap.
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 schema already documents normalization, emptiness, and maxLength, so the Args line ('The candidate string to validate') adds little. Baseline 3 applies since the schema does the heavy lifting; the description does not add format or syntax beyond 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?
States a specific verb+resource ('Check whether a string is structurally a valid IFSC') and immediately scopes it as offline/no-network. It explicitly distinguishes itself from the sibling `ifsc_lookup` by naming it and stating the boundary (validation vs. branch existence).
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 an explicit routing rule: 'Choose this over `ifsc_lookup` when you want to reject junk before spending a request', plus the when-not clause 'It does NOT confirm the branch exists - only `ifsc_lookup` does that.' Both the use case and the exclusion are stated, not implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_branchesSearch branches by bank, city, state or branch nameARead-onlyIdempotent
Find bank branches when you have a bank, city, state or branch name but no IFSC.
Filters are ANDed. At least one of bank_code, city, state or branch is
required - an unfiltered search is rejected with INVALID_INPUT because the
dataset has ~170k rows and no endpoint supports listing all of them.
Returns ok: true with: branches (each row shaped like an ifsc_lookup
result), count (total matches upstream), has_next (true when more pages
exist), and the echoed query.
Prefer this over bank_directory when the user wants a branch; use
bank_directory when they want a bank.
Args: bank_code: 4-letter bank code filter. city: City name filter. state: ISO 3166-2 state code filter, e.g. "IN-MH". branch: Exact branch-name filter. limit: Page size, 1-100 (default 10). offset: Row offset, for pagination.
Returns: Matching branches with pagination metadata, or a structured error payload.
Example: Ask "which Kotak branches are in Gurgaon?" -> call search_branches(bank_code="KKBK", city="GURGAON", limit=5).
| Name | Required | Description | Default |
|---|---|---|---|
| city | No | Optional city name exactly as the dataset spells it, e.g. 'MUMBAI', 'CHENNAI'. Case-insensitive. If no results come back, list the correct spelling with branch_places. | |
| limit | No | Rows to return, 1-100. Defaults to 10. | |
| state | No | Optional Indian state as an ISO 3166-2 code prefixed with 'IN-', e.g. 'IN-MH' for Maharashtra, 'IN-KA' Karnataka, 'IN-TN' Tamil Nadu. Not the full state name. List valid codes with branch_places. | |
| branch | No | Optional exact branch name as the dataset spells it, e.g. 'TULSIANI CHMBRS - NARIMAN PT'. Use branch_places to discover the exact spelling rather than guessing. | |
| offset | No | Row offset for paging through results. | |
| bank_code | No | Optional 4-letter bank code to filter by, e.g. 'HDFC', 'SBIN', 'KKBK'. Get one from bank_directory first. Uppercased for you. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly/idempotent/openWorld), the description discloses filter-combination semantics ('Filters are ANDed'), the rejection behavior and its error code (INVALID_INPUT with an unfiltered search), the reason (~170k rows, no list-all endpoint), and pagination fields (count, has_next). This is exactly the behavioral context annotations cannot carry.
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?
Front-loaded with purpose, requirement and sibling routing before the details, which is good structure. However, the 'Args' and 'Returns' blocks substantially duplicate the 100%-coverage schema and the output schema, so some sentences do not fully earn their place.
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?
Given six optional params, a rich output schema, and a need to disambiguate from bank_directory and branch_places, the description covers use case, preconditions, error behavior, return shape and a concrete example. An agent has everything needed to invoke it 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%, so the baseline is 3; the description earns above that by adding cross-parameter meaning absent from the schema — that filters are ANDed and that at least one filter must be supplied. The per-argument list largely restates the schema, so it does not climb higher.
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 line states a specific verb (Find) and resource (bank branches) plus the exact situation that triggers it ('when you have a bank, city, state or branch name but no IFSC'). It also explicitly contrasts itself with the sibling bank_directory, so an agent can route correctly without opening any 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?
Names the alternative (bank_directory) and the condition that selects it ('Prefer this... when the user wants a branch; use bank_directory when they want a bank'), and states the hard precondition that at least one of bank_code/city/state/branch is required. Exclusions and alternatives are both explicit.
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.
6 tool updates
v0.1.0- First observed
bank_directory - First observed
bank_metadata - First observed
branch_places - First observed
ifsc_lookup - First observed
ifsc_validate - First observed
search_branches
TDQS
Scored across 6 tools
Each tool has a clearly distinct purpose: offline validation vs. online lookup, branch search vs. place-name discovery, bank-name-to-code mapping vs. bank-level capabilities. The descriptions explicitly guide when to prefer one over another, eliminating overlap. No two tools appear to do the same thing.
Names mix conventions: ifsc_lookup and ifsc_validate use entity_action, search_branches uses verb_entity, and branch_places, bank_directory, bank_metadata use entity_noun. While all are snake_case and readable, there is no single predictable pattern. This makes the set feel slightly inconsistent despite clear individual names.
Six tools is well-scoped for an IFSC/bank lookup service. Each tool serves a distinct, non-redundant role, covering validation, lookup, search, discovery, directory, and metadata. No tool feels extraneous or missing at this count.
The surface covers the full read-only lifecycle: offline validation, online branch resolution, filtered search with pagination, place-name discovery to aid search, bank name-to-code mapping, and bank-level capabilities. No obvious dead ends remain for typical IFSC lookups. Minor gaps like reverse MICR lookup are outside the stated domain.
Maintenance
Related MCP Connectors
Cross-border payment & banking intelligence for AI agents: SWIFT/BIC, IBAN, sanctions, FX, tracking.
IBAN validation, extraction, format specs and BIC/SWIFT lookup tools for AI assistants.
- BankSyncOAuthio.banksync
Connect AI agents to bank accounts, transactions, balances, and investments.
Utility data for AI agents: IBAN, EU holidays, VAT rates, time zones, ECB FX. Pay per call.
Related MCP Servers
- AlicenseAqualityAmaintenanceIBAN validation across 89 countries, BIC/SWIFT and Swiss clearing lookup, batch validation, payment-reference, postal-address and Swiss QR-bill checks for AI agents. Connect via MCP or the REST API. Includes free quotas, paid credits and a Pro subscription. SEPA and country-risk indicators support payment-data checks; they do not confirm account ownership or replace beneficiary AML/KYC screening.13516 npm3MIT
- AlicenseNot gradedqualityBmaintenanceProvides AI agents with live access to a global directory of consumer and business banks, including community-rated scores across categories like customer service, fees, digital experience, and crypto friendliness, enabling grounded answers to banking questions.47 npm2MIT
- AlicenseAqualityCmaintenanceProvides AI agents with Indian fintech utilities including IFSC bank lookups, PAN/GSTIN validation, mutual fund NAVs, UPI VPA identification, pincode lookups, and INR formatting, all with zero authentication and zero cost.7MIT
- AlicenseAqualityDmaintenanceEnables LLMs to query real-time Indian bank branch details, postal PIN codes, validate GSTIN/PAN structures, check e-commerce serviceability, and compute GST breakdowns using free public APIs and offline verification logic.613 npm1MIT