Skip to main content
Glama
MADHAN-CJ

slash-dhan-mcp

by MADHAN-CJ

slash-dhan-mcp

An MCP server that gives an AI assistant (Claude or ChatGPT) read-only access to Indian market data from Dhan through one run_python tool. The model writes a Python script against the slashdhan library, the server runs it in an isolated subprocess and returns only what the script printed, so large data never enters the model's context. The same server hosts market watchers (model-authored conditions checked on a schedule without AI tokens, emailed when they become true) and a page where anyone can pause, resume or cancel them.

One Dhan session, owned by the operator, feeds every user: there are no user accounts and no OAuth. The operator pushes a fresh 24-hour Dhan access token every day through the admin API. The MCP endpoint is served unauthenticated at the host root, so this is built for an internal host.

How it fits together

Piece

What it does

src/slashdhan

Library the model's scripts import: feed-account reads, quotes, candles, instruments (every segment Dhan serves), Dhan's option chain with IV and greeks, expired-option history, indicators, market hours. Read-only by construction.

src/slash_dhan_mcp

MCP server (Streamable HTTP, no auth, served at the host root), tools run_python, feed_status, create_watcher, list_watchers; admin API; watchers page.

Feed

A single encrypted Dhan token in SQLite. PUT /admin/feed/token validates the token against Dhan before accepting it, records its expiry and data plan, and resumes watchers paused by the previous expiry.

Gateway

Local /dhan/... endpoint. Scripts hold only a per-run token; the gateway injects the Dhan headers, allows read endpoints only and applies Dhan's rate limits once for everyone.

Runner

Spawns python -I per script with a scrubbed environment, wall-clock timeout, output cap and a shared workspace that persists between calls. The scrip master is downloaded by the server once per day and served to scripts.

Watchers

SQLite-backed sweeper: baseline on first check, one alert per not-met→met transition, market-hours gate (NSE or MCX), pause when the feed token dies and auto-resume after the next token. Alerts go to the email given at creation.

Operators

Receive an email when the feed token expires and a warning before it does.

Dhan facts that shape the design: access tokens last 24 hours and the API offers no refresh; rate limits apply per token, so one limiter fronts all users; market data needs the paid Data-API plan on the feed account (error 806 otherwise); the option-chain endpoint allows one call per underlying and expiry every 3 seconds.

Related MCP server: MCP Python Interpreter

Requirements

  • Python 3.12 and uv.

  • A Dhan account with the Data-API subscription, and a way to generate its daily access token.

  • A Resend API key for alert and operator emails (optional in development: mails are logged instead).

Run locally

cp .env.example .env            # fill the two secrets and SLASH_DHAN_MCP_OPERATOR_EMAILS
uv sync
uv run slash-dhan-mcp           # MCP at http://127.0.0.1:8020/, watchers page at /watchers

Push today's token (the server validates it against Dhan first):

curl -X PUT http://127.0.0.1:8020/admin/feed/token \
  -H "Authorization: Bearer $SLASH_DHAN_MCP_ADMIN_SECRET" -H 'Content-Type: application/json' \
  -d '{"dhan_client_id": "1000000001", "access_token": "<24h token>"}'
curl http://127.0.0.1:8020/admin/feed/status -H "Authorization: Bearer $SLASH_DHAN_MCP_ADMIN_SECRET"

Add the MCP to claude.ai as a custom connector (authentication: none), or to ChatGPT (Plugins → Add custom MCP server, no authentication), with the server's public URL (https://host/). The watchers page is https://host/watchers.

Give the model SKILL.md: it is the document that teaches it how to write scripts against slashdhan (one composed script per question, return shapes, Dhan quirks, watcher contract). The tool description carries only a short reference. The server also serves it as https://host/slash-markets-dhan.zip (a folder slash-markets-dhan/SKILL.md, the shape claude.ai's skill upload and .claude/skills/ expect) and inline as https://host/SKILL.md; the watchers page links to both.

With DHAN_CLIENT_ID and DHAN_ACCESS_TOKEN in .env, uv run python scripts/live_smoke.py exercises the library against the real API without the server.

Deploy

deploy/ holds a hardened systemd unit, an nginx site, a one-time setup-uat.sh (service user, clone, venv, .env with fresh secrets, certificate) and update.sh (redeploy origin/main). deploy/remote.sh drives them over SSH and reads the box address from DEPLOY_HOST or the git-ignored file deploy/.host:

bash deploy/remote.sh setup                     # once
bash deploy/remote.sh deploy                    # after every push to main
echo "<24h token>" | bash deploy/remote.sh token <dhan_client_id>   # the daily ritual
bash deploy/remote.sh feed | urls | status | logs

The admin secret never leaves the box: token and feed read it from the box's .env.

Analytics

Set POSTHOG_API_KEY (a PostHog project key, phc_…; POSTHOG_HOST defaults to the US cloud) and the server reports product events to PostHog; unset, nothing leaves the box. The MCP has no user accounts, so events are personless and segmented only by host (claude / chatgpt / other, read from the request). Events: run_python (outcome, timing, script source, the slashdhan functions called, Dhan endpoint counts), feed_status, list_watchers, create_watcher (created or why not, script), watcher_fired, watcher_error, watcher_sweep, watcher_action (page pause/resume/cancel), watchers_page_viewed, skill_downloaded, feed_token_set / feed_token_rejected / feed_token_cleared, feed_expired and operator_mailed. The Dhan token, run tokens and alert email addresses are never sent. src/slash_dhan_mcp/analytics.py is the whole integration.

Development

uv run ruff check . && uv run ruff format --check . && uv run mypy && uv run pytest

The suite runs the library against an in-process fake Dhan and boots the real server against it: admin API, scripts through the gateway, feed expiry, operator mails, watcher lifecycle and the page and the analytics events. tests/test_skill.py pins every function named in SKILL.md to the library.

Licence

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables LLMs to manage Python environments and packages, execute Python code or scripts, and read or write files within a secured working directory. It also provides prompts for common Python tasks such as function creation, refactoring, and debugging.
    -
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables LLMs to run Python code and scripts across available environments, manage packages, and read/write files within a secured working directory. It also provides prompts and resources for common Python development tasks.
    -
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables LLMs to run Python code and scripts, manage Python environments and packages, and read or write files within an isolated working directory. It also provides prompts for common Python tasks such as function creation, refactoring, and debugging.
    -