angelone-mcp
An MCP (Model Context Protocol) server that wraps Angel One's SmartAPI
Documentation
angelone-mcp
An MCP (Model Context Protocol) server that wraps Angel One's SmartAPI —
trading, portfolio, market data, GTT rules, and margin/brokerage — so any MCP
client (Claude, Claude Code, etc.) can query your account and place orders
through natural conversation.
⚠️ This places real orders on a real trading account. Test with small
quantities first, and keep in mind Angel One (like most brokers) does not
let you "undo" a filled order.
What's included
- `angelone_mcp/client.py` – REST client for every documented SmartAPI route:
auth, orders, positions/holdings, GTT rules, historical candles/OI,
quotes, option greeks, gainers/losers, margin calculator, brokerage
estimator. Handles TOTP login, auto re-login on token expiry, and paces
itself against SmartAPI's documented rate limits (see "Rate limiting"
below).
- `angelone_mcp/server.py` – MCP server exposing 32 tools built on top of
the client (see full list below).
1. Prerequisites
- Python 3.10+
- An Angel One trading account with SmartAPI access
- A SmartAPI app created at https://smartapi.angelone.in/ (gives you an API key)
- TOTP set up on your Angel One account, and the base32 secret used to
set up that authenticator (not the 6-digit code — the secret behind it).
You get this once, when you first scan the QR code to enable TOTP; if you
don't have it saved, you'll need to reset/reconfigure TOTP on your account
to get a fresh secret.
2. Install
cd angelone-mcp
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt3. Configure credentials
Set these environment variables (e.g. in a `.env` file you source, or
directly in your MCP client config):
| Variable | Description |
|---|---|
| `ANGELONE_API_KEY` | API key from your SmartAPI app |
| `ANGELONE_CLIENT_CODE` | Your Angel One client/trading account code |
| `ANGELONE_PIN` | Your login PIN |
| `ANGELONE_TOTP_SECRET` | Base32 TOTP secret for your account |
Never commit these to source control. Treat `ANGELONE_TOTP_SECRET` and
`ANGELONE_PIN` like passwords — anyone with them plus your API key can trade
on your account.
Optional: running behind an HTTP proxy
If your machine/network requires an outbound HTTP proxy to reach the
internet, set:
| Variable | Description |
|---|---|
| `ANGELONE_HTTP_PROXY` | Proxy URL used for `http://` requests, e.g. `http://user:pass@proxyhost:8080` |
| `ANGELONE_HTTPS_PROXY` | Proxy URL used for `https://` requests (this is the one that matters — SmartAPI is https-only). Falls back to `ANGELONE_HTTP_PROXY` if unset. |
| `ANGELONE_NO_PROXY` | Optional comma-separated list of hosts to bypass the proxy for |
These are only needed if the standard `HTTP_PROXY` / `HTTPS_PROXY` environment
variables aren't already visible to the server process. That's commonly the
case for MCP servers, since MCP clients usually launch the server with an
explicit `env` block (like the JSON below) instead of inheriting your shell's
environment — so a proxy configured in your shell won't reach the server
unless you either add it to that `env` block yourself under `HTTPS_PROXY`, or
use the `ANGELONE_*` variables above. If neither `ANGELONE_HTTP_PROXY` nor
`ANGELONE_HTTPS_PROXY` is set, the server falls back to the standard
`HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` variables automatically.
4. Run it
Standalone (for testing):
python -m angelone_mcp.serverIt speaks MCP over stdio, so it's meant to be launched by an MCP client, not
run interactively.
Claude Desktop / Claude Code config
Add to your MCP client's config (e.g. `claude_desktop_config.json`):
{
"mcpServers": {
"angelone": {
"command": "/absolute/path/to/angelone-mcp/.venv/bin/python",
"args": ["-m", "angelone_mcp.server"],
"cwd": "/absolute/path/to/angelone-mcp",
"env": {
"ANGELONE_API_KEY": "your_api_key",
"ANGELONE_CLIENT_CODE": "your_client_code",
"ANGELONE_PIN": "your_pin",
"ANGELONE_TOTP_SECRET": "your_base32_totp_secret",
"ANGELONE_HTTPS_PROXY": "http://user:pass@proxyhost:8080"
}
}
}
}Tools exposed
Session
`login`, `logout`, `get_profile`
Orders
`place_order`, `modify_order`, `cancel_order`, `get_order_book`,
`get_trade_book`, `get_individual_order_details`
Portfolio / funds
`get_positions`, `get_holdings`, `get_all_holdings`, `get_rms_limit`,
`convert_position`
GTT (Good Till Triggered) rules
`gtt_create_rule`, `gtt_modify_rule`, `gtt_cancel_rule`, `gtt_details`,
`gtt_list`
Market data
`get_ltp`, `get_market_quote`, `search_scrip`, `get_candle_data`,
`get_oi_data`, `get_option_greeks`, `get_gainers_losers`,
`get_put_call_ratio`, `get_oi_buildup`, `get_nse_intraday_data`,
`get_bse_intraday_data`
Margin & brokerage
`get_margin`, `estimate_charges`
How auth works
`AngelOneClient` logs in lazily on the first tool call using
`clientcode` + `pin` + a TOTP generated on the fly from
`ANGELONE_TOTP_SECRET` (via `pyotp`). It caches the resulting `jwtToken`,
`refreshToken`, and `feedToken` in memory for the life of the process. If any
call comes back with a 401/403 or a `TokenException`, it transparently
re-logs-in once and retries — you don't need to call `login` yourself unless
you want to force a fresh session.
Sessions issued by SmartAPI are valid until midnight IST regardless of
activity, so a long-running server may still need a fresh login the next day
— the auto-retry logic handles that automatically on the next call.
Session persistence across restarts
A successful login is also cached to a file on disk, so a fresh server
process doesn't need a fresh TOTP-based login every time it starts (handy
since TOTP requires the code to be freshly generated — restarting the server
several times in a row otherwise means several real logins in a row).
On startup, before serving any tool calls, the server calls
`AngelOneClient.restore_session()`, which:
1. Looks for a previously saved session file. If there isn't one, it does
nothing further — the client stays in its normal lazy mode and logs in on
the first tool call, same as before this feature existed.
2. If a saved session is found, it loads the cached tokens and verifies them
with a real `getProfile` call.
3. If that verification succeeds, the restored session is used as-is — no
fresh login needed.
4. If it fails for any reason (expired token, revoked session, corrupt file,
etc.), the cached tokens are discarded and a normal fresh login runs
instead.
Every successful login (fresh or via the automatic 401/403 retry described
above) re-saves the session file, so it stays current across the whole time
the server runs, not just at startup. `logout` deletes the file.
| Variable | Description |
|---|---|
| `ANGELONE_SESSION_PERSIST` | Set to `false`/`0`/`no`/`off` to disable session persistence entirely (default: enabled) |
| `ANGELONE_SESSION_FILE` | Override the file path used to persist the session. Default: a file under the OS temp directory, named from a hash of your client code (so multiple accounts on the same machine don't collide) |
The session file holds a live access token — not your PIN or TOTP secret,
but enough to call the API as you until it expires. It's written with
owner-only file permissions where the OS supports it; treat it as sensitive
the same way you'd treat any cached login session.
Rate limiting
`AngelOneClient` paces every outgoing call against
SmartAPI's documented per-endpoint rate limits
— login and most portfolio reads at 1 request/sec, `getProfile` at 3/sec,
quotes/GTT/order-detail lookups at 10/sec, order placement at 20/sec, and so
on. Limits are per SmartAPI endpoint, not global, so calling different tools
back-to-back is never slowed down by this — only a *repeat* call to the same
endpoint made faster than SmartAPI's own limit allows gets held back, which
you'd want anyway.
If SmartAPI reports its own limit was hit regardless (HTTP 403/429, "Access
denied because of exceeding access rate"), the call backs off and retries a
few times with increasing delay before giving up — and that response no
longer gets misread as an expired session and doesn't trigger a spurious
extra login the way it used to.
This applies to every tool automatically; there's nothing to configure to
get it. To turn client-side pacing off entirely (SmartAPI still enforces its
own limits server-side either way — this only controls whether the client
tries to stay under them proactively):
| Variable | Description |
|---|---|
| `ANGELONE_RATE_LIMIT_DISABLED` | Set to `true`/`1`/`yes`/`on` to disable proactive pacing (default: enabled) |
Testing
pip install -e ".[test]"
# Offline: verifies the server registers the expected tools. No credentials
# or network access needed.
python -m pytest tests/test_tool_registration.py -v
# Offline: unit tests for session persistence (login state cached to disk,
# restored + verified via get_profile on restart, falls back to a fresh
# login when the cache is missing/invalid). Uses a fake HTTP layer - no
# credentials or network access needed.
python -m pytest tests/test_session_persistence.py -v
# Offline: unit tests for AngelOneClient's own rate limiting (pacing per
# ROUTE_MIN_INTERVAL, backoff/retry on a 403/429 rate-limit response, and
# that such a response is never misread as an expired session). Uses a fake
# HTTP layer - no credentials or network access needed.
python -m pytest tests/test_client_rate_limiting.py -v
# Live, read-only smoke test against your real account. Calls get_profile,
# get_order_book, get_holdings, search_scrip, get_ltp, etc. through the
# actual MCP server subprocess, plus a check that a session survives a
# restart of the server without calling the "login" tool again. Never calls
# place_order/modify_order/cancel_order/gtt_create_rule/gtt_modify_rule/
# gtt_cancel_rule/convert_position/logout - a SafeSession wrapper
# hard-asserts those are never invoked. On top of the server's own rate
# limiting (see "Rate limiting" above), the test itself also paces its tool
# calls and backs off/retries if the API reports one was hit anyway (see
# "Rate limiting in the live test" below) - belt and suspenders. Requires
# ANGELONE_API_KEY/ANGELONE_CLIENT_CODE/ANGELONE_PIN/ANGELONE_TOTP_SECRET
# to be set; skips automatically if they aren't.
python -m pytest tests/test_readonly_live.py -v -s
# or, for a plain-text report without pytest:
python tests/test_readonly_live.pyRate limiting in the live test
The live test (`tests/test_readonly_live.py`) calls a real account against
the real SmartAPI. The server it drives already paces itself (see "Rate
limiting" above), but the test adds its own independent pacing on top -
useful because it also exercises things the server-side limiter doesn't see
by itself, like two separate server subprocesses (the session-persistence
check) hitting the same account back to back:
- A `RateLimiter` tracks the last time each MCP tool was called and, before
calling it again, waits out the rest of that endpoint's minimum interval
(1/req-per-second-limit, plus a ~20% safety margin). Distinct tools hit
distinct SmartAPI endpoints with independent limits, so this only ever
delays a *repeat* call to the same tool (e.g. `get_profile` being called
again by the second server spawn in the session-persistence check) - a
normal single pass through the suite, where every tool is called once or
twice, isn't slowed down by it in practice.
- If SmartAPI reports a rate limit was hit anyway (HTTP 403, "Access denied
because of exceeding access rate"), the test backs off and retries a
couple of times with increasing delay instead of failing outright.
- This governs the test suite's own request pace only - it has no effect on
how the MCP server behaves for a real MCP client (Claude, etc.); SmartAPI
still enforces its limits server-side either way.
Notes / limitations
- Order params (`price`, `quantity`, etc.) are passed as strings, matching
what SmartAPI's `placeOrder` expects.
- `get_margin` and `estimate_charges` take a list of position/order dicts —
see the SmartAPI docs for exact field names per instrument type
(https://smartapi.angelone.in/docs/Margin, .../Brokerage).
- Rate limits are enforced by Angel One per endpoint; see
https://smartapi.angelone.in/docs/RateLimit. This server does not do its
own client-side rate limiting.
- Not affiliated with or endorsed by Angel One / Angel Broking.
Frequently asked questions
What is angelone-mcp?
angelone-mcp is An MCP (Model Context Protocol) server that wraps Angel One's SmartAPI
How do I install angelone-mcp?
Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.
Is angelone-mcp open source?
Yes — it is hosted on GitHub at https://github.com/pyalgobot/angelone-mcp.
Related MCP tools
Transparent and Efficient Financial Analysis
Fast and Accurate Code Search for Agents. Uses 99% fewer tokens than grep+read
TradingView MCP server — real-time market data, technical analysis, screeners & backtesting for Claude, ChatGPT, Cursor & any MCP client. Stocks, crypto, forex & futures across global exchanges. Hosted or self-host.
Open source AI clip generator: turns long videos into viral 9:16 shorts with AI moment detection, face tracking, subtitles and dubbing. Self-host free with Docker (MIT), or use the cloud with GPU speed from $12/mo. MCP server and API for AI agents.
Control Gmail, Google Calendar, Docs, Sheets, Slides, Chat, Forms, Tasks, Search & Drive with AI - Comprehensive Google Workspace MCP Server & CLI Tool
Cut AI token costs 95%+ on code exploration. The leading MCP server for precise, symbol-level GitHub code retrieval via tree-sitter AST. Works with Claude Code, Cursor & any MCP client. 313B+ tokens saved.
Run your own MCP server? See who uses it and what to fix.
Measure it with TrackMCP