Skip to main content
Glama
ofsazib

dse-mcp

by ofsazib
README.md
# dse-mcp

[![PyPI](https://img.shields.io/pypi/v/dse-mcp)](https://pypi.org/project/dse-mcp/)
[![Python](https://img.shields.io/pypi/pyversions/dse-mcp)](https://pypi.org/project/dse-mcp/)
[![CI](https://github.com/ofsazib/dse-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/ofsazib/dse-mcp/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![MCP](https://img.shields.io/badge/MCP-server-blue)](https://modelcontextprotocol.io)

A production-grade **Model Context Protocol (MCP) server for the Dhaka Stock
Exchange (DSE)** — live quotes, price history, fundamentals, order-book depth,
news, technical indicators, screening — plus a bundled **`dse-analysis` skill**
that teaches your agent a disciplined buy/sell workflow on top of those tools.

Data is scraped from [dsebd.org](https://www.dsebd.org) (DSE's official site) with
all of its production quirks handled: incomplete TLS chains, `--` placeholders,
comma-grouped numbers, invisible layout tables, market-hours-aware caching, and a
mirror-domain fallback. **Informational only — not investment advice. Not
affiliated with DSE.**

## Install

Requires Python 3.12+ (or just `uv`/`pipx`, which manage Python for you).

```bash
# Register with Claude Code (recommended)
claude mcp add dse -s user -- uvx dse-mcp
```

<details>
<summary>Other clients</summary>

**Claude Desktop** (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "dse": { "command": "uvx", "args": ["dse-mcp"] }
  }
}
```

**Cursor** (`~/.cursor/mcp.json`): same JSON shape as Claude Desktop.

**pipx:**

```bash
pipx install dse-mcp
# then register with the command "dse-mcp"
```

**Docker:**

```bash
docker run -i --rm ghcr.io/ofsazib/dse-mcp
```

</details>

## The dse-analysis skill

The repo ships an agent skill that turns these tools into a repeatable
buy/sell analysis workflow (market context → fundamentals → technicals → news →
verdict with confidence + risks):

```bash
# from a git clone of this repo
./scripts/install_skill.sh              # installs to ~/.claude, ~/.agents, ~/.zcode
./scripts/install_skill.sh --dir ~/x    # add a custom skills directory
./scripts/install_skill.sh --uninstall  # remove from all configured directories
```

Or copy `skills/dse-analysis/` into your client's skills directory manually.

## Removing everything

```bash
claude mcp remove dse                    # unregister the MCP server (add -s user if needed)
./scripts/install_skill.sh --uninstall   # remove the skill from all skill directories
uv cache clean dse-mcp                   # drop uvx's cached build of the package
```

## Tools (16)

Every response carries `market_status` (live/closed), `data_freshness`
(live/snapshot/stale), and `data_as_of` — so closed-market data is never mistaken
for live prices.

### Market
| Tool | Description |
|---|---|
| `market_status` | Live/closed, session hours, Dhaka time, next session |
| `market_summary` | DSEX/DS30/DSES + changes, totals for the latest session |
| `get_index_history(days)` | Daily index history (rolling one-year archive) |
| `top_movers(category)` | `gainers` \| `losers` \| `most_active` \| `turnover` |
| `sector_performance` | Advancing/declining + avg change per sector |

### Stock
| Tool | Description |
|---|---|
| `search_symbols(query)` | Fuzzy search over 636 bundled instruments (22 sectors) |
| `get_quote(symbol)` | LTP, day range, close, change, trades, value, volume |
| `get_price_history(symbol, start, end, days)` | Day-end OHLCV, oldest-first |
| `get_company_profile(symbol)` | Capital structure, listing year, 52w range, EPS block |
| `get_fundamentals(symbol)` | Analysis-ready: EPS, P/E, dividends, reserves, 52w |
| `get_market_depth(symbol)` | Live bid/ask levels + session stats |
| `get_news(symbol, days, limit)` | Company or market-wide news/disclosures |
| `get_pe_ratios()` | P/E table (interim/audited/trailing) for all instruments |

### Analysis & screening
| Tool | Description |
|---|---|
| `compute_indicators(symbol, ...)` | SMA50/200, RSI(14), MACD, Bollinger |
| `technical_summary(symbol)` | Trend, support/resistance, outlook + reasons |
| `screen_stocks(sector, pe_max, price_range, volume_min)` | Filter the live board |

## Trading-hours aware caching

- Market **live** (Sun–Thu 10:00–14:50 Asia/Dhaka): data cached 30s (depth is
  always fetched fresh).
- Market **closed**: the last session's snapshot is cached ~1h and **persisted to
  `~/.cache/dse-mcp/`**, so a restart still serves the last session (labeled
  `snapshot`).
- DSE unreachable: the persisted snapshot is served labeled `stale`.

## Configuration (optional)

All via environment variables with the `DSE_MCP_` prefix — see
[.env.example](.env.example). Useful ones:

| Variable | Default | Purpose |
|---|---|---|
| `DSE_MCP_RATE_LIMIT` | `5.0` | Outbound requests/second (token bucket) |
| `DSE_MCP_CACHE_TTL_OPEN` | `30` | Seconds, while the market is live |
| `DSE_MCP_CACHE_DIR` | `~/.cache/dse-mcp` | Snapshot persistence |
| `DSE_MCP_LOG_LEVEL` | `WARNING` | Logs go to stderr (never stdout — MCP) |

## Troubleshooting

- **`[SSL: CERTIFICATE_VERIFY_FAILED]`** — dsebd.org serves an incomplete TLS
  chain. dse-mcp uses the OS trust store (`truststore`), which handles it; do not
  replace it with `verify=False`.
- **Empty depth / quotes look frozen** — the market is closed (or a holiday).
  Check `market_status` and the response's `data_as_of`.
- **`DGEN: null`** — the DGEN index is retired from DSE's live pages; null is the
  honest value.

## Development

```bash
git clone https://github.com/ofsazib/dse-mcp && cd dse-mcp
uv sync                                   # Python 3.12 via uv
uv run pytest                             # 44 offline tests (recorded fixtures)
uv run ruff check .
uv run dse-mcp                            # run the server locally
npx @modelcontextprotocol/inspector uv run dse-mcp   # visual debugging
```

Re-record fixtures when dsebd.org markup changes: `uv run python scripts/record_fixtures.py`.
Rebuild the instrument bundle: `uv run python scripts/build_instruments.py`.
See [AGENTS.md](AGENTS.md) for architecture and [CONTRIBUTING.md](CONTRIBUTING.md)
to contribute.

## Legal

- Data source: [dsebd.org](https://www.dsebd.org) (Dhaka Stock Exchange). This
  project is an independent, unaffiliated open-source client; data remains the
  property of DSE.
- Nothing here is investment advice. DSE securities can be illiquid and subject to
  circuit breakers; verify everything against primary sources before acting.

## License

[MIT](LICENSE) © Omar Faruk Sazib

TDQS

A3.6/5.0

Scored across 16 tools

Disambiguation4/5

Each tool targets a distinct data aspect (quotes, history, fundamentals, news, technicals), so most boundaries are clear. However, get_company_profile, get_fundamentals, and get_pe_ratios share overlapping metrics like EPS and P/E, which could cause confusion if an agent relies on descriptions alone.

Naming Consistency4/5

The naming pattern is mostly get_ for retrievals and noun phrases for snapshots (market_summary, top_movers), which is readable and predictable. Minor deviations like search_symbols versus get_index_history and compute_indicators versus technical_summary keep it from being fully uniform.

Tool Count4/5

With 16 tools, the server is on the upper end but still well-scoped for a stock exchange data platform. Each tool earns its place by covering a meaningful query type—pricing, fundamentals, news, technicals—without unnecessary fragmentation or duplication.

Completeness4/5

The surface covers the core domain well: market state, indices, quotes, history, fundamentals, news, screening, and technical analysis. Minor omissions like a direct list-all-instruments endpoint or corporate actions beyond dividends are workable through search_symbols and screen_stocks, so no severe dead ends exist.

Maintenance

ActivityMaintained
ResponsivenessNo issues