from __future__ import annotations import importlib.util import sys from pathlib import Path SCRIPT_PATH = Path(__file__).resolve().parents[1] / "check_broker_reference_inventory.py" SPEC = importlib.util.spec_from_file_location("check_broker_reference_inventory", SCRIPT_PATH) assert SPEC is not None and SPEC.loader is not None checker = importlib.util.module_from_spec(SPEC) sys.modules[SPEC.name] = checker SPEC.loader.exec_module(checker) def _write(path: Path, text: str = "ok") -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text, encoding="utf-8") def _write_valid_capture(root: Path) -> tuple[Path, Path]: mcp = root / "broker-mcp-current-20260706T000000Z" kotak = root / "kotak-neo-api-v2-current-latest" groww = root / "groww-trade-api-docs" indstocks = root / "indstocks-api-docs" _write( mcp / "sources.txt", "\n".join( [ "dhan https://docs.dhanhq.co/mcp/", "upstox https://upstox.com/developer/api-documentation/mcp-integration/", "groww https://groww.in/updates/groww-mcp", ] ), ) _write( mcp / "SHA256SUMS", "\n".join(["abc raw/dhan.html", "abc raw/upstox.html", "abc raw/groww.html"]), ) _write(mcp / "raw/dhan.headers.txt") _write(mcp / "raw/upstox.headers.txt") _write(mcp / "raw/groww.headers.txt") _write( mcp / "raw/dhan.html", "Model Context Protocol native MCP server supported clients trade, manage your portfolio", ) _write( mcp / "raw/upstox.html", "https://mcp.upstox.com/mcp Read-Only Access Daily re-authorization Claude Desktop ChatGPT Cursor VS Code", ) _write( mcp / "raw/groww.html", "https://mcp.groww.in/mcp GrowwMCP mcp-remote@0.1.18 52155 DDPI No background syncing " "No data storage on AI servers stocks and F&O", ) _write(kotak / "README.md") _write(kotak / "SHA256SUMS") _write(kotak / "raw/index.html") content_files = [ "content/curl-examples.md", "content/holdings.md", "content/instruments.md", "content/limits.md", "content/login-with-totp.md", "content/margins.md", "content/order-report-apis.md", "content/order/cancel-order.md", "content/order/modify-order.md", "content/order/place-order.md", "content/positions.md", "content/quotes.md", "content/trade-api-faq .md", "content/websocket/websocket.md", ] _write(kotak / "content-paths.txt", "\n".join(content_files)) for rel in content_files: _write(kotak / "raw" / rel) _write( kotak / "raw/content/login-with-totp.md", "tradeApiLogin tradeApiValidate Authorization neo-fin-key TOTP MPIN", ) _write(kotak / "raw/content/curl-examples.md", "Auth Sid neo-fin-key Quotes do not send ts") _write( kotak / "raw/content/order/cancel-order.md", "/quick/order/cancel /quick/order/bo/exit /quick/order/co/exit neo-fin-key trading symbol", ) _write(kotak / "raw/content/instruments.md", "pTrdSymbol 'ts' field Authorization") _write(kotak / "raw/content/quotes.md", "plain token Authorization nse_cm") _write(kotak / "raw/content/websocket/websocket.md", "Connect to Order feed Session Token sid Data Center") for rel in [ "inventories/00-docs.json", "inventories/02-api-keys.json", "inventories/03-python-sdk.json", "inventories/04-curl-docs.json", "public-docs-extract/curl-historical-data.json", "public-docs-extract/curl-instruments.json", "public-docs-extract/curl-live-data.json", "public-docs-extract/curl-margin.json", "public-docs-extract/curl-orders.json", "public-docs-extract/curl-portfolio.json", "public-docs-extract/curl-smart-orders.json", "public-docs-extract/curl-user.json", ]: _write(groww / rel) _write(groww / "public-docs-extract/api-keys.json", "Groww Cloud | API Keys static-ip-api-trading-setup") _write(groww / "public-docs-extract/python-sdk-intro.json", "pip install growwapi GrowwAPI.get_access_token api_key secret pyotp") _write( groww / "public-docs-extract/python-sdk-orders.json", "place_order modify_order cancel_order get_order_status_by_reference get_order_detail", ) _write(groww / "public-docs-extract/python-sdk-live-data.json", "get_quote market_depth open_interest") _write( groww / "public-docs-extract/python-sdk-historical-data.json", "get_historical_candle_data interval_in_minutes candles", ) _write(groww / "public-docs-extract/python-sdk-instruments.json", "instruments.csv exchange_token trading_symbol") _write( groww / "public-docs-extract/python-sdk-portfolio.json", "get_holdings_for_user get_positions_for_user trading_symbol", ) _write( groww / "public-docs-extract/python-sdk-margin.json", "get_available_margin_details get_order_margin_details fno_margin_details", ) _write( groww / "public-docs-extract/python-sdk-smart-orders.json", "create_smart_order cancel_smart_order SMART_ORDER_TYPE_GTT SMART_ORDER_TYPE_OCO", ) _write(groww / "public-docs-extract/python-sdk-feed.json", "GrowwFeed subscribe_ltp subscribe_market_depth feed.consume") _write(groww / "public-docs-extract/python-sdk-user.json", "get_user_profile vendor_user_id active_segments") for rel in [ "inventories/04-api-overview.json", "inventories/06-users-auth.json", "inventories/07-instruments-master.json", "inventories/08-market-quote.json", "inventories/09-historical-data.json", "inventories/10-websockets.json", "inventories/11-normal-orders.json", "inventories/12-smart-orders-gtt.json", "inventories/13-margin-calculation.json", "inventories/14-portfolio-funds.json", "inventories/15-utility.json", "inventories/17-faq.json", "public-docs-extract/api-overview.json", ]: _write(indstocks / rel) _write(indstocks / "public-docs-extract/faq.json", "pip install indstocks-sdk npm install indstocks-sdk") _write(indstocks / "public-docs-extract/users-auth.json", "GET /user/profile GET /funds Authorization: YOUR_ACCESS_TOKEN") _write(indstocks / "public-docs-extract/instruments-master.json", "GET /market/instruments security_id instruments.csv") _write( indstocks / "public-docs-extract/market-quote.json", "GET /market/quotes/full GET /market/quotes/ltp live_price market_depth", ) _write(indstocks / "public-docs-extract/historical-data.json", "GET /market/historical/{interval} 1minute candles") _write( indstocks / "public-docs-extract/websockets.json", "wss://ws-prices.indstocks.com/api/v1/ws/prices " "wss://ws-order-updates.indstocks.com/api/v1/ws/trades order_updates", ) _write(indstocks / "public-docs-extract/normal-orders.json", "POST /order POST /order/modify POST /order/cancel algo_id") _write( indstocks / "public-docs-extract/smart-orders-gtt.json", "POST /smart/order POST /smart/order/cancel sl_trigger_price tgt_trigger_price", ) _write(indstocks / "public-docs-extract/margin-calculation.json", "GET /margin total_margin brokerage") _write( indstocks / "public-docs-extract/portfolio-funds.json", "GET /portfolio/holdings GET /portfolio/positions pnl_absolute", ) _write(indstocks / "public-docs-extract/utility.json", "GET /option-chain GET /option-chain-symbols") return mcp, kotak def test_validate_inventory_accepts_complete_public_reference_captures(tmp_path: Path) -> None: _write_valid_capture(tmp_path) results = checker.validate_inventory(tmp_path) assert results assert all(result.ok for result in results), [result.message for result in results] def test_validate_inventory_reports_missing_markers(tmp_path: Path) -> None: _write_valid_capture(tmp_path) (tmp_path / "broker-mcp-current-20260706T000000Z/raw/upstox.html").write_text( "https://mcp.upstox.com/mcp Claude Desktop ChatGPT Cursor VS Code", encoding="utf-8", ) results = checker.validate_inventory(tmp_path) failures = [result.message for result in results if not result.ok] assert failures == [ "MCP page markers: raw/upstox.html: missing marker 'read-only access', " "raw/upstox.html: missing marker 'daily re-authorization'" ] def test_validate_inventory_uses_latest_timestamped_mcp_capture(tmp_path: Path) -> None: old_mcp, _ = _write_valid_capture(tmp_path) new_mcp = tmp_path / "broker-mcp-current-20260706T010000Z" for path in old_mcp.rglob("*"): if path.is_file(): target = new_mcp / path.relative_to(old_mcp) _write(target, path.read_text(encoding="utf-8")) (new_mcp / "raw/groww.html").write_text("stale", encoding="utf-8") results = checker.validate_inventory(tmp_path) failures = [result.message for result in results if not result.ok] assert failures == [ "MCP page markers: raw/groww.html: missing marker 'https://mcp.groww.in/mcp', " "raw/groww.html: missing marker 'growwmcp', " "raw/groww.html: missing marker 'mcp-remote@0.1.18', " "raw/groww.html: missing marker '52155', " "raw/groww.html: missing marker 'ddpi', " "raw/groww.html: missing marker 'no background syncing', " "raw/groww.html: missing marker 'no data storage on ai servers', " "raw/groww.html: missing marker 'stocks and f&o'" ] def test_validate_inventory_reports_missing_api_doc_markers(tmp_path: Path) -> None: _write_valid_capture(tmp_path) (tmp_path / "indstocks-api-docs/public-docs-extract/websockets.json").write_text( "wss://ws-prices.indstocks.com/api/v1/ws/prices", encoding="utf-8", ) results = checker.validate_inventory(tmp_path) failures = [result.message for result in results if not result.ok] assert failures == [ "INDstocks API page markers: public-docs-extract/websockets.json: missing marker " "'wss://ws-order-updates.indstocks.com/api/v1/ws/trades', " "public-docs-extract/websockets.json: missing marker 'order_updates'" ]