India Finance MCP
Pricing
Pay per usage
India Finance MCP
MCP server for Indian financial data. Get real-time mutual fund NAVs from AMFI (47,000+ schemes), validate GSTIN numbers with checksum verification, and more. Built for AI agents (Claude, ChatGPT, Cursor) via MCP protocol. REST API also available. Stock quotes coming in v1.1.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Tushar
Actor stats
0
Bookmarked
1
Total users
0
Monthly active users
3 days ago
Last modified
Categories
Share
India Finance MCP
A Model Context Protocol (MCP) server exposing Indian financial data to LLMs and agents, with a parallel REST API for non-MCP consumers.
Single TypeScript codebase, two deployment targets:
- MCP server (FastMCP, Node) — deployed as an Apify Actor. Plug it into Claude Desktop, Cursor, or any MCP-compatible client.
- REST mirror (Hono on Cloudflare Workers) — for HTTP clients, monetizable via RapidAPI.
Live as of 2026-05-04.
| Target | URL | Transports |
|---|---|---|
| Apify Actor | https://apify.com/vamppog/india-finance-mcp | MCP (Streamable HTTP) at /mcp via FastMCP |
| Cloudflare Workers | https://india-finance-mcp.vamp-fa0.workers.dev | MCP (JSON-RPC) at POST /mcp + REST at /api/* |
What it does
| Tool | Purpose | Source | Auth needed |
|---|---|---|---|
get_mutual_fund_nav | Look up the latest NAV for any Indian mutual fund by AMFI scheme code or free-text scheme name. | AMFI — daily refresh | None |
validate_gstin | Structural + checksum validation of a 15-character GSTIN. Parses out state, embedded PAN, entity code. | Offline (mod-36 checksum) | None |
Planned for v1.1:
| Tool | Purpose | Source |
|---|---|---|
get_stock_quote | Latest quote for NSE/BSE-listed equities. | Yahoo Finance proxy (RELIANCE.NS-style symbols) |
Quickstart
As an MCP client (Claude Desktop, Cursor, etc.)
You have three options:
Option A — Cloudflare Workers (always-on, no Apify standby needed):
POST https://india-finance-mcp.vamp-fa0.workers.dev/mcp
Speaks MCP Streamable HTTP / JSON-RPC. Works with any MCP client that supports HTTP transport.
Option B — Apify Actor (FastMCP-backed):
Once the actor's standby mode is enabled in the Apify console, point your MCP client at the actor's MCP endpoint at /mcp.
Option C — local stdio:
git clone <this-repo>cd "Indian finance MCP"npm installnpm run dev:mcp # stdio transport for local MCP clients
For an interactive UI, use the FastMCP inspector:
$npm run inspect # opens fastmcp dev UI
As an MCP client over plain HTTP (curl)
# initializecurl -X POST https://india-finance-mcp.vamp-fa0.workers.dev/mcp \-H 'Content-Type: application/json' \-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'# tools/listcurl -X POST https://india-finance-mcp.vamp-fa0.workers.dev/mcp \-H 'Content-Type: application/json' \-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'# tools/callcurl -X POST https://india-finance-mcp.vamp-fa0.workers.dev/mcp \-H 'Content-Type: application/json' \-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"validate_gstin","arguments":{"gstin":"27AAPFU0939F1ZV"}}}'
As a REST client
# Mutual fund NAV by AMFI scheme codecurl 'https://india-finance-mcp.vamp-fa0.workers.dev/api/mutual-fund/nav?code=122639'# Free-text scheme name searchcurl 'https://india-finance-mcp.vamp-fa0.workers.dev/api/mutual-fund/nav?q=parag+parikh+flexi+cap+direct&limit=5'# GSTIN validationcurl 'https://india-finance-mcp.vamp-fa0.workers.dev/api/gstin/validate?gstin=27AAPFU0939F1ZV'
Sample response (NAV):
{"query": "122639","matches": [{"schemeCode": "122639","isinGrowth": "INF879O01027","isinReinvestment": null,"schemeName": "Parag Parikh Flexi Cap Fund - Direct Plan - Growth","nav": 91.2902,"date": "30-Apr-2026","category": "Open Ended Schemes(Equity Scheme - Flexi Cap Fund)","amc": "PPFAS Mutual Fund"}],"asOf": "30-Apr-2026","source": "amfi"}
Sample response (GSTIN):
{"gstin": "27AAPFU0939F1ZV","valid": true,"errors": [],"parts": {"stateCode": "27","stateName": "Maharashtra","pan": "AAPFU0939F","entityCode": "1","defaultLetter": "Z","checksum": "V"}}
Architecture
src/├── core/ # Pure logic + I/O. Workers-safe. No fastmcp/hono imports.│ ├── amfi.ts # Fetch + parse + 1h TTL cache + search for AMFI NAV file│ ├── gstin.ts # Mod-36 checksum, state-code table, structural checks│ └── types.ts # Shared record/result types├── tools/ # MCP-tool contracts: zod schema + execute() + _TOOL const│ ├── mutual-fund-nav.ts│ └── validate-gstin.ts├── mcp/│ └── index.ts # FastMCP entry — addTool(...) per tool, stdio or http-stream└── rest/├── app.ts # Hono app — REST routes + POST /mcp (JSON-RPC)├── mcp-handler.ts # Workers-safe MCP JSON-RPC dispatcher (no fastmcp)├── worker.ts # Cloudflare Workers entry: { fetch: app.fetch }└── node.ts # Local-dev entry via @hono/node-server
How a tool is added
- Write the logic in
src/core/— pure functions and any caching. Keep this layer dependency-free; it has to run on both Node (Apify) and the Workers runtime. - Define the contract in
src/tools/— export a zod schema, anexecutefunction, and a*_TOOLconst that bundles them with the tool's MCP name + description. This contract is reused by both entries. - Wire it into MCP in
src/mcp/index.tsviaserver.addTool({...}). Tool results must be returned as a JSON string. - Wire it into REST in
src/rest/app.ts— add a route, parse query params with<schema>.safeParse, callexecute, returnc.json(...).
This is the rule the codebase optimizes for: one logic implementation, one contract, two transports.
Transport detection (Apify / FastMCP entry)
src/mcp/index.ts chooses transport at startup:
| Trigger | Transport | Use case |
|---|---|---|
APIFY_CONTAINER_PORT set | httpStream on that port | Apify Actor runtime |
FASTMCP_TRANSPORT=http-stream | httpStream on PORT (default 8080) | Self-hosted HTTP |
| Neither | stdio | Claude Desktop, local CLI, FastMCP inspector |
MCP on Cloudflare Workers (no FastMCP)
The Workers deploy serves MCP via a hand-rolled JSON-RPC handler in src/rest/mcp-handler.ts because FastMCP pulls in Node-only deps that don't bundle for Workers. The handler implements initialize, notifications/initialized (acked with HTTP 202), ping, tools/list, and tools/call. Tool input schemas are emitted from the same zod schemas used by the FastMCP entry, via zod-to-json-schema. Both entries therefore expose identical tool surface — only the framework underneath differs.
Stateless: no session id, no SSE stream. Each POST is one JSON-RPC request and returns one JSON response (or 202 for notifications).
Caching
getNavRecords() in core/amfi.ts memoizes the parsed AMFI file with a 1-hour in-memory TTL plus an inflight request de-dupe (concurrent calls share a single fetch promise).
- On Node/Apify the cache lives across requests for the lifetime of the process.
- On Cloudflare Workers it lives per isolate — cold isolates re-fetch. For cross-isolate caching, bind a KV namespace in
wrangler.toml(a commented stub is already there) and short-circuit before the AMFI fetch.
AMFI publishes the daily NAV file once per business day around 9–11 PM IST.
Module-resolution gotcha
tsconfig.json uses moduleResolution: "Bundler" with verbatimModuleSyntax: true. Imports between src/ files must end in .js even though the source is .ts:
import { getNavRecords } from "../core/amfi.js"; // ✅import { getNavRecords } from "../core/amfi"; // ❌ build will fail
Both tsc and Wrangler/esbuild resolve this correctly.
Workers compatibility constraint
src/rest/worker.ts and its transitive imports must be Workers-safe:
- ✅ Global
fetch, pure logic, Hono. - ❌
process.env,@hono/node-server, anything fromsrc/mcp/, anything fromfastmcp. FastMCP pulls in Node-only deps that won't bundle for Workers.
The current import graph through worker.ts → app.ts → tools/* → core/{amfi,gstin}.ts honors this; keep it that way.
Development
Setup
$npm install # ~225 packages
Common commands
npm test # all vitest testsnpx vitest run tests/gstin.test.ts -t "checksum" # single test by file + namenpx vitest # watch modenpx tsc --noEmit # typecheck onlynpm run dev:mcp # tsx watch — stdio MCP servernpm run inspect # fastmcp dev — interactive MCP inspector UInpm run dev:rest # tsx watch — Hono on http://localhost:3000npm run cf:dev # wrangler dev — Workers runtime on http://localhost:8787npx tsx scripts/smoke.ts # end-to-end smoke test against live AMFI
Building
The dist/ build is only needed for the Apify Docker image. Dockerfile runs tsc in the builder stage. Wrangler bundles src/rest/worker.ts directly — no pre-build required for CF Workers.
$npm run build # tsc → dist/
Testing approach
tests/amfi.test.tsparses a fixture string covering category headers, AMC headers, blank;;;;;rows, andN.A.NAV rows. Extend the fixture rather than mocking the fetch.tests/gstin.test.tscovers valid GSTINs, length/format/state/PAN errors, and bad-checksum cases. Bad-state and bad-PAN tests recompute a valid checksum first to isolate the error under test — important when adding new validation rules._resetCacheForTesting()is exported fromcore/amfi.tsif a future test needs to re-exercise the fetch path.
Deployment
Apify (MCP server)
npm install -g apify-cliapify loginapify push --wait-for-finish 600
Configuration lives in .actor/actor.json. The webServerMcpPath field tells Apify to route MCP traffic to /mcp. The Dockerfile is multi-stage (Alpine, build → runtime) and starts the server with FASTMCP_TRANSPORT=http-stream and FASTMCP_STATELESS=true.
After the build succeeds, enable Standby mode in the Apify console to make the MCP endpoint addressable. That step is console-only — no CLI equivalent.
Cloudflare Workers (REST mirror)
npx wrangler loginnpx wrangler deploy
wrangler.toml pins account_id (set per-developer; default in this repo is the Vamp account). To use a different account, override with CLOUDFLARE_ACCOUNT_ID or edit the file.
The build is a single bundle (~190 KiB / ~38 KiB gzipped). No KV bindings yet — adding cross-isolate caching is the obvious next infra change.
Project structure
.├── .actor/ # Apify actor metadata (actor.json, input_schema.json)├── src/ # See architecture diagram above├── tests/ # vitest suites├── scripts/smoke.ts # End-to-end smoke test├── Dockerfile # Multi-stage build for Apify├── wrangler.toml # Cloudflare Workers config├── tsconfig.json # Bundler-mode + verbatimModuleSyntax├── vitest.config.ts└── package.json
Data sources & legal notes
- AMFI NAV file (
https://www.amfiindia.com/spages/NAVAll.txt) — public, no auth, attribution recommended. Thesource: "amfi"field is included in every NAV response. - GSTIN validation — entirely offline. The mod-36 checksum is the public algorithm published by GSTN. This tool does not call the GSTN portal and does not confirm whether a GSTIN is currently active or registered to any specific entity. It only confirms that the string is structurally well-formed.
- Stock quotes (planned) — Yahoo Finance has no official free terms-of-use; use at your own risk and consider swapping to a paid provider before commercializing the stock-quote tool.
Roadmap
- v0.1 —
get_mutual_fund_nav,validate_gstin, deployed to Apify + CF Workers - v1.1 —
get_stock_quote(Yahoo Finance proxy, NSE + BSE) - v1.2 —
get_market_summary(indices snapshot) - Infra — KV-backed NAV cache for cross-isolate persistence on Workers
- Listings — Apify Store
- Listings — Smithery, Glama, mcp.so, PulseMCP, official MCP registry
License
MIT