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-mcp look 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: india-business-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"].
Inputs: limit, query. Returns: object
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").
Inputs: bank_code (required). Returns: object
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").
Inputs: bank_code (required), limit, state, district. Returns: object
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").
Inputs: ifsc (required). Returns: object
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".
Inputs: ifsc (required). Returns: object
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).
Inputs: city, limit, state, branch, +2 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
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.
Connect AI agents to bank accounts, transactions, balances, and investments.
Bank financials, central-bank rates, enforcement actions and fair-lending data as agent tools.
Related MCP Servers
- 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.69 npm1MIT
- AlicenseNot gradedqualityCmaintenanceEnables Indian bank branch IFSC code lookup via Razorpay's open API, allowing AI agents to retrieve branch details using IFSC codes.136 npmMIT
- AlicenseAqualityBmaintenanceEnables AI agents to validate IBANs, generate synthetic checksum-valid test IBANs, and look up bank registry records by BIC or domestic identifier, with optional paid capabilities for bank, BIC, and SEPA Instant/VoP reachability.6MIT