Skip to main content
Glama
TradeMCP

TradeMCP

by Aaditya2502

TradeMCP

An MCP (Model Context Protocol) server that exposes trading analytics — technical indicators, portfolio state, risk metrics, and backtest results — as tools an LLM agent can call. Built to demonstrate protocol-level MCP understanding, not just API wrapping.

The defining design choice: read operations are open; write operations (placing trades) sit behind a human-in-the-loop approval gate. Phase 1 (this repo) ships the read-only surface and the architecture that makes the read/write split clean.


Why this exists

Most MCP portfolio projects wrap a public API in a decorator. This one is built around the questions that actually come up when you put an agent in front of something that can move money:

  • What happens when the model sends a hallucinated symbol or malformed arguments?

  • How do you stop an agent from doing something irreversible?

  • How do you keep tool outputs small enough to not blow the context window?

  • How do you swap simulated data for a live brokerage without rewriting the tools?

See DESIGN.md for the full decision record.


Related MCP server: FinClaw

Architecture

┌──────────────┐   MCP (stdio / streamable HTTP)   ┌────────────────────┐
│  LLM client  │ ───────────────────────────────►  │   TradeMCP server  │
│ (Claude etc.)│ ◄───────────────────────────────  │     (server.py)    │
└──────────────┘        tool calls / results        └─────────┬──────────┘
                                                               │ depends on interface
                                                               ▼
                                              ┌──────────────────────────────┐
                                              │   MarketDataProvider (ABC)    │
                                              ├──────────────────────────────┤
                                              │ SimulatedProvider  (default)  │
                                              │ KiteProvider       (Phase 2)  │  ← live AutoTrade Bot
                                              └──────────────────────────────┘

Module

Responsibility

server.py

Protocol/tool layer: validate input → call provider → format output

data_provider.py

Data layer behind an abstract interface (the swap point for live data)

models.py

Pydantic input schemas — the first line of defense against bad LLM input

formatting.py

Shared markdown/JSON formatting (context-efficient output)

errors.py

Centralized, actionable error messages

The tool layer depends on the MarketDataProvider interface, never on a concrete data source. That dependency-inversion boundary is what lets the same server run on simulated data in CI and live data in production.


Tools (Phase 1 — all read-only)

Tool

Purpose

trade_get_indicators

Latest RSI / EMA / MACD / ATR + signal for a symbol

trade_get_portfolio

Holdings, cash, equity, unrealized P&L

trade_get_portfolio_risk

Concentration, beta, VaR, Sharpe, drawdown, circuit breakers

trade_list_backtests

Paginated list of backtest runs (discover run IDs)

trade_get_backtest

Full metrics for one run

Every tool is annotated readOnlyHint: true and supports both markdown (default, human-readable) and json (structured) output.


Quickstart

# 1. Install
pip install -e ".[dev]"

# 2. Run the test suite
pytest

# 3. Run the server (stdio transport, the default)
python -m trade_mcp.server

# Or over HTTP for remote clients:
TRADE_MCP_TRANSPORT=streamable_http TRADE_MCP_PORT=8000 python -m trade_mcp.server

Inspect with the MCP Inspector

npx @modelcontextprotocol/inspector python -m trade_mcp.server

Wire into Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "trade-mcp": {
      "command": "python",
      "args": ["-m", "trade_mcp.server"],
      "cwd": "/absolute/path/to/trade-mcp/src"
    }
  }
}

Then ask: "What's the RSI on Reliance, and how concentrated is my portfolio?"


Data is simulated (by design)

The default SimulatedMarketDataProvider returns deterministic data derived from a hash of each input, so demos and tests are fully reproducible with zero credentials. Wiring in the live AutoTrade Bot backend means implementing the five MarketDataProvider methods in a new class and changing one line in server.py.


Roadmap

  • Phase 1 — Read-only analytics surface, provider abstraction, tests

  • Phase 2 — trade_place_order behind a human approval gate (Telegram confirmation + audit log)

  • Phase 3 — Rate limiting, response caching, retries

  • Phase 4 — Multi-server client demonstrating tool discovery/orchestration

  • Phase 5 — Live KiteMarketDataProvider wrapping the real trading bot


License

MIT

Available Tools

5 tools
trade_get_backtestA
Read-onlyIdempotent

Get full metrics for a single backtest run by its ID.

Use trade_list_backtests first to find a valid run_id, then call this for the detailed performance breakdown (returns, Sharpe/Sortino, drawdown, win rate, profit factor, capital curve endpoints).

Args: params (GetBacktestInput): Validated input containing: - run_id (str): Run identifier from trade_list_backtests - response_format (ResponseFormat): "markdown" (default) or "json"

Returns: str: Markdown summary, or JSON with this schema: { "run_id": str, "strategy": str, "symbol": str, "ran_at": str, "period": {"start": str, "end": str}, "metrics": { "total_return_pct": float, "annualized_return_pct": float, "sharpe_ratio": float, "sortino_ratio": float, "max_drawdown_pct": float, "win_rate_pct": float, "total_trades": int, "profit_factor": float }, "starting_capital": float, "ending_capital": float } On failure (including unknown run_id): "Error: "

Examples: - "How did bt_ema_crossover_reliance_0427 perform?" -> that run_id - Don't guess run IDs — list them first with trade_list_backtests.

Inputs: params (required). Returns: result

trade_get_indicatorsA
Read-onlyIdempotent

Get the latest technical indicators for a single NSE symbol.

Returns a snapshot of common momentum/trend indicators (RSI, EMAs, MACD, ATR) plus a derived bullish/bearish signal. Read-only; computes nothing on live capital.

Args: params (GetIndicatorsInput): Validated input containing: - symbol (str): NSE ticker, case-insensitive (e.g. "RELIANCE") - response_format (ResponseFormat): "markdown" (default) or "json"

Returns: str: Markdown summary, or JSON with this schema: { "symbol": str, # normalized uppercase ticker "as_of": str, # ISO 8601 UTC timestamp "last_price": float, "indicators": { "rsi_14": float, "ema_20": float, "ema_50": float, "macd": float, "macd_signal": float, "macd_histogram": float, "atr_14": float }, "signal": str # "bullish" | "bearish" } On failure: "Error: "

Examples: - "What's the RSI on Infosys?" -> symbol="INFY" - "Is Reliance bullish right now?" -> symbol="RELIANCE" - Don't use to place a trade — this tool is read-only analytics.

Inputs: params (required). Returns: result

trade_get_portfolioA
Read-onlyIdempotent

Get the current portfolio: holdings, cash, equity, and unrealized P&L.

Read-only snapshot of all open positions with per-position and aggregate profit/loss.

Args: params (PortfolioInput): Validated input containing: - response_format (ResponseFormat): "markdown" (default) or "json"

Returns: str: Markdown summary, or JSON with this schema: { "as_of": str, # ISO 8601 UTC timestamp "cash_balance": float, "invested": float, "market_value": float, "total_equity": float, "unrealized_pnl": float, "position_count": int, "positions": [ { "symbol": str, "quantity": int, "avg_cost": float, "last_price": float, "invested": float, "market_value": float, "unrealized_pnl": float, "unrealized_pnl_pct": float } ] } On failure: "Error: "

Examples: - "How is my portfolio doing?" -> default markdown - "Give me my positions as JSON" -> response_format="json"

Inputs: params (required). Returns: result

trade_get_portfolio_riskA
Read-onlyIdempotent

Get aggregate risk metrics for the current portfolio.

Surfaces concentration, beta, Value-at-Risk, Sharpe, drawdown, risk-budget usage, and any open circuit breakers. Read-only.

Args: params (PortfolioRiskInput): Validated input containing: - response_format (ResponseFormat): "markdown" (default) or "json"

Returns: str: Markdown summary, or JSON with this schema: { "as_of": str, # ISO 8601 UTC timestamp "total_equity": float, "max_position_concentration_pct": float, "portfolio_beta": float, "value_at_risk_95_1d": float, "sharpe_ratio_30d": float, "max_drawdown_pct": float, "open_circuit_breakers": [str], "risk_budget_used_pct": float } On failure: "Error: "

Examples: - "How concentrated is my portfolio?" -> read concentration_pct - "What's my 1-day VaR?" -> read value_at_risk_95_1d

Inputs: params (required). Returns: result

trade_list_backtestsA
Read-onlyIdempotent

List available backtest runs, most useful for discovering run IDs.

Returns a paginated list of run summaries. Pair this with trade_get_backtest to drill into a specific run's full metrics.

Args: params (ListBacktestsInput): Validated input containing: - limit (Optional[int]): Max runs to return, 1-50 (default 10) - offset (Optional[int]): Runs to skip for pagination (default 0) - response_format (ResponseFormat): "markdown" (default) or "json"

Returns: str: Markdown summary, or JSON with this schema: { "total": int, # total runs available "count": int, # runs in this page "offset": int, # current offset "items": [ { "run_id": str, "strategy": str, "symbol": str, "ran_at": str, "total_return_pct": float, "sharpe_ratio": float } ], "has_more": bool, "next_offset": int | null # offset for the next page, if any } On failure: "Error: "

Examples: - "What backtests have I run?" -> default page - "Show me the next 10 backtests" -> offset=10

Inputs: params (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.6/5.0

    Scored across 5 tools

    Disambiguation5/5

    Each tool targets a distinct area: backtest details, indicators, portfolio snapshot, portfolio risk, and backtest listing. No overlap in functionality.

    Naming Consistency5/5

    All tool names follow a consistent verb_noun pattern prefixed with 'trade_' (e.g., trade_get_backtest, trade_list_backtests).

    Tool Count5/5

    5 tools cover the core analytical needs of a trading server: portfolio overview, risk assessment, indicators, and backtesting. Well-scoped.

    Completeness4/5

    The server focuses on read-only analytics; missing order placement or historical price data, but core portfolio, risk, indicators, and backtest coverage is solid.

    Maintenance

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      C
      quality
      D
      maintenance
      An MCP server that exposes the Jesse algorithmic trading framework's capabilities to LLM agents for backtesting, optimization, and risk analysis. It provides 32 specialized tools for managing trading strategies and performing comprehensive market simulations via the Jesse REST API.
      69
      23
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server that provides AI agents with financial tools including real-time quotes, backtesting, technical analysis, and multi-exchange data via a simple CLI interface.
      2
      MIT
    • A
      license
      B
      quality
      C
      maintenance
      An MCP server that gives an LLM agent a typed, audited tool surface over quant crypto-options desk analytics: gamma exposure, vanna, skew, vol surface, options flow, technicals, portfolio greeks, scenario analysis, and live positions.
      22
      2
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      MCP server that exposes the Backtest360 backtesting engine API as tools, enabling AI agents to conversationally discover indicators, build and validate strategies, run backtests, and read results.
      14
      82 PyPI
      MIT