<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[angelone-mcp]]></title><description><![CDATA[<h1>angelone-mcp</h1>
<p dir="auto"><a href="https://mcpservers.org/servers/pyalgobot/angelone-mcp.git" target="_blank" rel="noopener noreferrer nofollow ugc">AngelOne MCP server</a><br />
<a href="https://github.com/pyalgobot/angelone-mcp" target="_blank" rel="noopener noreferrer nofollow ugc">GitHub Repo</a></p>
<p dir="auto">An MCP (Model Context Protocol) server that wraps <a href="https://smartapi.angelone.in/docs" target="_blank" rel="noopener noreferrer nofollow ugc">Angel One's SmartAPI</a> —<br />
trading, portfolio, market data, GTT rules, and margin/brokerage — so any MCP<br />
client (Claude, Claude Code, etc.) can query your account and place orders<br />
through natural conversation.</p>
<p dir="auto">⚠️ <strong>This places real orders on a real trading account.</strong> Test with small<br />
quantities first, and keep in mind Angel One (like most brokers) does not<br />
let you "undo" a filled order.</p>
<h2>What's included</h2>
<ul>
<li><code>angelone_mcp/client.py</code> – REST client for every documented SmartAPI route:<br />
auth, orders, positions/holdings, GTT rules, historical candles/OI,<br />
quotes, option greeks, gainers/losers, margin calculator, brokerage<br />
estimator. Handles TOTP login, auto re-login on token expiry, and paces<br />
itself against SmartAPI's documented rate limits (see "Rate limiting"<br />
below).</li>
<li><code>angelone_mcp/server.py</code> – MCP server exposing 32 tools built on top of<br />
the client (see full list below).</li>
</ul>
<h2>1. Prerequisites</h2>
<ul>
<li>Python 3.10+</li>
<li>An Angel One trading account with SmartAPI access</li>
<li>A SmartAPI app created at <a href="https://smartapi.angelone.in/" target="_blank" rel="noopener noreferrer nofollow ugc">https://smartapi.angelone.in/</a> (gives you an API key)</li>
<li>TOTP set up on your Angel One account, and the <strong>base32 secret</strong> used to<br />
set up that authenticator (not the 6-digit code — the secret behind it).<br />
You get this once, when you first scan the QR code to enable TOTP; if you<br />
don't have it saved, you'll need to reset/reconfigure TOTP on your account<br />
to get a fresh secret.</li>
</ul>
<h2>2. Install</h2>
<pre><code class="language-bash">cd angelone-mcp
python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r requirements.txt
</code></pre>
<h2>3. Configure credentials</h2>
<p dir="auto">Set these environment variables (e.g. in a <code>.env</code> file you source, or<br />
directly in your MCP client config):</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ANGELONE_API_KEY</code></td>
<td>API key from your SmartAPI app</td>
</tr>
<tr>
<td><code>ANGELONE_CLIENT_CODE</code></td>
<td>Your Angel One client/trading account code</td>
</tr>
<tr>
<td><code>ANGELONE_PIN</code></td>
<td>Your login PIN</td>
</tr>
<tr>
<td><code>ANGELONE_TOTP_SECRET</code></td>
<td>Base32 TOTP secret for your account</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>Never commit these to source control.</strong> Treat <code>ANGELONE_TOTP_SECRET</code> and<br />
<code>ANGELONE_PIN</code> like passwords — anyone with them plus your API key can trade<br />
on your account.</p>
<h3>Optional: running behind an HTTP proxy</h3>
<p dir="auto">If your machine/network requires an outbound HTTP proxy to reach the<br />
internet, set:</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ANGELONE_HTTP_PROXY</code></td>
<td>Proxy URL used for <code>http://</code> requests, e.g. <code>http://user:pass@proxyhost:8080</code></td>
</tr>
<tr>
<td><code>ANGELONE_HTTPS_PROXY</code></td>
<td>Proxy URL used for <code>https://</code> requests (this is the one that matters — SmartAPI is https-only). Falls back to <code>ANGELONE_HTTP_PROXY</code> if unset.</td>
</tr>
<tr>
<td><code>ANGELONE_NO_PROXY</code></td>
<td>Optional comma-separated list of hosts to bypass the proxy for</td>
</tr>
</tbody>
</table>
<p dir="auto">These are only needed if the standard <code>HTTP_PROXY</code> / <code>HTTPS_PROXY</code> environment<br />
variables aren't already visible to the server process. That's commonly the<br />
case for MCP servers, since MCP clients usually launch the server with an<br />
explicit <code>env</code> block (like the JSON below) instead of inheriting your shell's<br />
environment — so a proxy configured in your shell won't reach the server<br />
unless you either add it to that <code>env</code> block yourself under <code>HTTPS_PROXY</code>, or<br />
use the <code>ANGELONE_*</code> variables above. If neither <code>ANGELONE_HTTP_PROXY</code> nor<br />
<code>ANGELONE_HTTPS_PROXY</code> is set, the server falls back to the standard<br />
<code>HTTP_PROXY</code>/<code>HTTPS_PROXY</code>/<code>NO_PROXY</code> variables automatically.</p>
<h2>4. Run it</h2>
<p dir="auto">Standalone (for testing):</p>
<pre><code class="language-bash">python -m angelone_mcp.server
</code></pre>
<p dir="auto">It speaks MCP over stdio, so it's meant to be launched by an MCP client, not<br />
run interactively.</p>
<h3>Claude Desktop / Claude Code config</h3>
<p dir="auto">Add to your MCP client's config (e.g. <code>claude_desktop_config.json</code>):</p>
<pre><code class="language-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"
      }
    }
  }
}
</code></pre>
<h2>Tools exposed</h2>
<p dir="auto"><strong>Session</strong><br />
<code>login</code>, <code>logout</code>, <code>get_profile</code></p>
<p dir="auto"><strong>Orders</strong><br />
<code>place_order</code>, <code>modify_order</code>, <code>cancel_order</code>, <code>get_order_book</code>,<br />
<code>get_trade_book</code>, <code>get_individual_order_details</code></p>
<p dir="auto"><strong>Portfolio / funds</strong><br />
<code>get_positions</code>, <code>get_holdings</code>, <code>get_all_holdings</code>, <code>get_rms_limit</code>,<br />
<code>convert_position</code></p>
<p dir="auto"><strong>GTT (Good Till Triggered) rules</strong><br />
<code>gtt_create_rule</code>, <code>gtt_modify_rule</code>, <code>gtt_cancel_rule</code>, <code>gtt_details</code>,<br />
<code>gtt_list</code></p>
<p dir="auto"><strong>Market data</strong><br />
<code>get_ltp</code>, <code>get_market_quote</code>, <code>search_scrip</code>, <code>get_candle_data</code>,<br />
<code>get_oi_data</code>, <code>get_option_greeks</code>, <code>get_gainers_losers</code>,<br />
<code>get_put_call_ratio</code>, <code>get_oi_buildup</code>, <code>get_nse_intraday_data</code>,<br />
<code>get_bse_intraday_data</code></p>
<p dir="auto"><strong>Margin &amp; brokerage</strong><br />
<code>get_margin</code>, <code>estimate_charges</code></p>
<h2>How auth works</h2>
<p dir="auto"><code>AngelOneClient</code> logs in lazily on the first tool call using<br />
<code>clientcode</code> + <code>pin</code> + a TOTP generated on the fly from<br />
<code>ANGELONE_TOTP_SECRET</code> (via <code>pyotp</code>). It caches the resulting <code>jwtToken</code>,<br />
<code>refreshToken</code>, and <code>feedToken</code> in memory for the life of the process. If any<br />
call comes back with a 401/403 or a <code>TokenException</code>, it transparently<br />
re-logs-in once and retries — you don't need to call <code>login</code> yourself unless<br />
you want to force a fresh session.</p>
<p dir="auto">Sessions issued by SmartAPI are valid until midnight IST regardless of<br />
activity, so a long-running server may still need a fresh login the next day<br />
— the auto-retry logic handles that automatically on the next call.</p>
<h3>Session persistence across restarts</h3>
<p dir="auto">A successful login is also cached to a file on disk, so a fresh server<br />
process doesn't need a fresh TOTP-based login every time it starts (handy<br />
since TOTP requires the code to be freshly generated — restarting the server<br />
several times in a row otherwise means several real logins in a row).</p>
<p dir="auto">On startup, before serving any tool calls, the server calls<br />
<code>AngelOneClient.restore_session()</code>, which:</p>
<ol>
<li>Looks for a previously saved session file. If there isn't one, it does<br />
nothing further — the client stays in its normal lazy mode and logs in on<br />
the first tool call, same as before this feature existed.</li>
<li>If a saved session is found, it loads the cached tokens and verifies them<br />
with a real <code>getProfile</code> call.</li>
<li>If that verification succeeds, the restored session is used as-is — no<br />
fresh login needed.</li>
<li>If it fails for any reason (expired token, revoked session, corrupt file,<br />
etc.), the cached tokens are discarded and a normal fresh login runs<br />
instead.</li>
</ol>
<p dir="auto">Every successful login (fresh or via the automatic 401/403 retry described<br />
above) re-saves the session file, so it stays current across the whole time<br />
the server runs, not just at startup. <code>logout</code> deletes the file.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ANGELONE_SESSION_PERSIST</code></td>
<td>Set to <code>false</code>/<code>0</code>/<code>no</code>/<code>off</code> to disable session persistence entirely (default: enabled)</td>
</tr>
<tr>
<td><code>ANGELONE_SESSION_FILE</code></td>
<td>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)</td>
</tr>
</tbody>
</table>
<p dir="auto">The session file holds a live access token — not your PIN or TOTP secret,<br />
but enough to call the API as you until it expires. It's written with<br />
owner-only file permissions where the OS supports it; treat it as sensitive<br />
the same way you'd treat any cached login session.</p>
<h2>Rate limiting</h2>
<p dir="auto"><code>AngelOneClient</code> paces every outgoing call against<br />
<a href="https://smartapi.angelone.in/docs/RateLimit" target="_blank" rel="noopener noreferrer nofollow ugc">SmartAPI's documented per-endpoint rate limits</a><br />
— login and most portfolio reads at 1 request/sec, <code>getProfile</code> at 3/sec,<br />
quotes/GTT/order-detail lookups at 10/sec, order placement at 20/sec, and so<br />
on. Limits are per SmartAPI endpoint, not global, so calling different tools<br />
back-to-back is never slowed down by this — only a <em>repeat</em> call to the same<br />
endpoint made faster than SmartAPI's own limit allows gets held back, which<br />
you'd want anyway.</p>
<p dir="auto">If SmartAPI reports its own limit was hit regardless (HTTP 403/429, "Access<br />
denied because of exceeding access rate"), the call backs off and retries a<br />
few times with increasing delay before giving up — and that response no<br />
longer gets misread as an expired session and doesn't trigger a spurious<br />
extra login the way it used to.</p>
<p dir="auto">This applies to every tool automatically; there's nothing to configure to<br />
get it. To turn client-side pacing off entirely (SmartAPI still enforces its<br />
own limits server-side either way — this only controls whether the client<br />
tries to stay under them proactively):</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ANGELONE_RATE_LIMIT_DISABLED</code></td>
<td>Set to <code>true</code>/<code>1</code>/<code>yes</code>/<code>on</code> to disable proactive pacing (default: enabled)</td>
</tr>
</tbody>
</table>
<h2>Testing</h2>
<pre><code class="language-bash">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.py
</code></pre>
<h3>Rate limiting in the live test</h3>
<p dir="auto">The live test (<code>tests/test_readonly_live.py</code>) calls a real account against<br />
the real SmartAPI. The server it drives already paces itself (see "Rate<br />
limiting" above), but the test adds its own independent pacing on top -<br />
useful because it also exercises things the server-side limiter doesn't see<br />
by itself, like two separate server subprocesses (the session-persistence<br />
check) hitting the same account back to back:</p>
<ul>
<li>A <code>RateLimiter</code> tracks the last time each MCP tool was called and, before<br />
calling it again, waits out the rest of that endpoint's minimum interval<br />
(1/req-per-second-limit, plus a ~20% safety margin). Distinct tools hit<br />
distinct SmartAPI endpoints with independent limits, so this only ever<br />
delays a <em>repeat</em> call to the same tool (e.g. <code>get_profile</code> being called<br />
again by the second server spawn in the session-persistence check) - a<br />
normal single pass through the suite, where every tool is called once or<br />
twice, isn't slowed down by it in practice.</li>
<li>If SmartAPI reports a rate limit was hit anyway (HTTP 403, "Access denied<br />
because of exceeding access rate"), the test backs off and retries a<br />
couple of times with increasing delay instead of failing outright.</li>
<li>This governs the test suite's own request pace only - it has no effect on<br />
how the MCP server behaves for a real MCP client (Claude, etc.); SmartAPI<br />
still enforces its limits server-side either way.</li>
</ul>
<h2>Notes / limitations</h2>
<ul>
<li>Order params (<code>price</code>, <code>quantity</code>, etc.) are passed as strings, matching<br />
what SmartAPI's <code>placeOrder</code> expects.</li>
<li><code>get_margin</code> and <code>estimate_charges</code> take a list of position/order dicts —<br />
see the SmartAPI docs for exact field names per instrument type<br />
(<a href="https://smartapi.angelone.in/docs/Margin" target="_blank" rel="noopener noreferrer nofollow ugc">https://smartapi.angelone.in/docs/Margin</a>, .../Brokerage).</li>
<li>Rate limits are enforced by Angel One per endpoint; see<br />
<a href="https://smartapi.angelone.in/docs/RateLimit" target="_blank" rel="noopener noreferrer nofollow ugc">https://smartapi.angelone.in/docs/RateLimit</a>. This server does not do its<br />
own client-side rate limiting.</li>
<li>Not affiliated with or endorsed by Angel One / Angel Broking.</li>
</ul>
]]></description><link>https://smartapi.angelone.in/smartapi/forum/topic/5658/angelone-mcp</link><generator>RSS for Node</generator><lastBuildDate>Mon, 31 Aug 2026 05:32:45 GMT</lastBuildDate><atom:link href="https://smartapi.angelone.in/smartapi/forum/topic/5658.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 29 Aug 2026 10:55:16 GMT</pubDate><ttl>60</ttl></channel></rss>