mcp-hkexnews
by agentladle
README.md
# AgentLadle MCP HKEXnews
**English** | [δΈζ](README_zh.md)
> π¨π³/ππ° Cloud-hosted MCP for A-share & HK listed companies (Past 3 years annual & latest interim reports). [Read more](https://github.com/achuan101/agentladle-mcp) | [Get API Key](https://agentladle.com/register)
A [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that provides tools for **discovering, downloading, parsing, and searching** Hong Kong listed company announcements from [HKEXnews](https://www.hkexnews.hk).
It enables AI assistants (Claude, Cursor, etc.) to access HKEXnews announcement data through 6 structured tools β from discovering available announcements to keyword-searching within their pages.
> **Scope (v0.1):** Announcements and disclosures except full periodic report PDFs (Annual / Interim / Quarterly Report and ESG Report under `t1=40000`). Performance announcements (Final / Interim / Quarterly Results) are included.
## Features
- **6 MCP tools** for HKEXnews announcement data: state-driven retrieval (search directly, fallback to download/parse only when needed)
- **PDF document parsing** using [PyMuPDF](https://pymupdf.readthedocs.io/) β physical page extraction into page-split JSON
- **Local keyword search** with TF + position-boost scoring, zero external search dependencies
- **Idempotent** β already-downloaded/parsed files are automatically skipped
- **Zero-config install** β one line to add to your MCP client, no clone or manual setup needed
- **Pure Python**, cross-platform (Windows / macOS / Linux)
## Prerequisites
- **Python 3.10+** β [Download Python](https://www.python.org/downloads/)
- **uv** β [Install uv](https://docs.astral.sh/uv/getting-started/installation/)
> **Note:** After installing uv, restart your terminal and MCP client (e.g. Cherry Studio) to ensure the `uv` command is recognized.
## Quick Start
Add to your MCP client configuration (Claude Desktop, Cursor, etc.):
```json
{
"mcpServers": {
"mcp-hkexnews": {
"command": "uvx",
"args": ["agentladle-mcp-hkexnews"]
}
}
}
```
That's it. `uvx` will automatically download the package and its dependencies from PyPI β no clone, no manual install, no path configuration.
### Alternative: pip install
If you prefer managing the environment yourself:
```bash
pip install agentladle-mcp-hkexnews
```
Then configure:
```json
{
"mcpServers": {
"mcp-hkexnews": {
"command": "agentladle-mcp-hkexnews"
}
}
}
```
### Alternative: Run from source (local development)
Clone the repository and run directly:
```bash
git clone https://github.com/agentladle/mcp-hkexnews.git
```
Then configure your MCP client:
```json
{
"mcpServers": {
"mcp-hkexnews": {
"command": "uv",
"args": ["run", "--directory", "/path/to/mcp-hkexnews", "agentladle-mcp-hkexnews"]
}
}
}
```
Replace `/path/to/mcp-hkexnews` with the actual path to the cloned repository.
## Data Flow
```
HKEXnews API Local Files (~/.agentladle/mcp-hkexnews/data/)
ββββββββββββββ ββββββββββββββββββββββββββββββ
activestock_sehk_e.json βββ companies.json (stock_codeβstockId mapping)
ListOfSecurities.xlsx βββ β
tierone/tiertwo JSON βββ tiers.json (headline category mapping)
β
titleSearchServlet.do βββ pdf/{LOCAL_KEY}/ (Tool 2: primary PDF/HTML + manifest)
β
PyMuPDF parsing βββ json/*.json (Tool 3: parse, page-split)
β
Local TF search βββ search results (Tool 4: keyword search)
Page range read βββ page content (Tool 5: read pages)
```
## Tools
| # | Tool | Description |
|---|------|-------------|
| 1 | `list_hkexnews_announcements` | Discover available HKEXnews announcements for a company |
| 2 | `download_hkexnews_announcement` | Download announcement PDF (HTML fallback); idempotent |
| 3 | `parse_hkexnews_announcement` | Parse PDF/HTML into page-split JSON using PyMuPDF |
| 4 | `keyword_search` | Full-text keyword search with TF relevance scoring |
| 5 | `get_announcement_pages` | Read announcement content by page number range |
| 6 | `lookup_stock_code` | **Diagnostic**: look up stock_codeβstockId mapping when resolution fails |
### Tool 1: `list_hkexnews_announcements`
List available HKEXnews announcements for a company. Use this tool ONLY when the exact date/title is unspecified by the user, or when a download attempt fails due to an ambiguous match. Excludes full periodic report PDFs (Annual / Interim / Quarterly Report and ESG Report under `t1=40000`).
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `stock_code` | string | β
| 5-digit HK stock code, e.g. `"00700"` |
| `category` | string | β | HKEX t1/t2 code or tier name, e.g. `"Inside Information"`, `"13500"`, `"20000"`. Omit to list all in-scope categories |
| `start_date` | string | β | Start date `YYYY-MM-DD` |
| `end_date` | string | β | End date `YYYY-MM-DD` |
| `title_keyword` | string | β | Title keyword filter |
| `limit` | int | β | Max announcements to return, default 10, max 50 |
### Tool 2: `download_hkexnews_announcement`
Download a specific HKEXnews announcement from www1.hkexnews.hk. Prefer `local_key` from `list_hkexnews_announcements` when available. Idempotent.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `stock_code` | string | β
| 5-digit HK stock code, e.g. `"00700"` |
| `release_date` | string | β | Release date `YYYY-MM-DD` (optional if `local_key` provided) |
| `title_keyword` | string | β | Title substring to disambiguate same-day announcements |
| `category` | string | β | Optional category filter |
| `news_id` | string | β | HKEXnews NEWS_ID if known |
| `local_key` | string | β | Exact local bundle key from list results |
### Tool 3: `parse_hkexnews_announcement`
Parse a downloaded announcement PDF/HTML into page-split JSON. Uses PyMuPDF for PDF physical-page text extraction.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `local_key` | string | β
| Bundle key returned by list/download, e.g. `"00700_13500_2026-03-15_a1b2c3d4"` |
### Tool 4: `keyword_search`
Full-text keyword search across all pages. Results ranked by TF + position-boost score.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `local_key` | string | β
| Bundle key |
| `keywords` | string[] | β
| 1β5 search keywords |
| `match_mode` | string | β | `"ANY"` (default, any keyword matches) / `"ALL"` (all must match) |
| `max_results` | int | β | Max results to return, default 5, max 50 |
### Tool 5: `get_announcement_pages`
Read full page content by page number range.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `local_key` | string | β
| Bundle key |
| `start_page` | int | β
| Start page number (1-based) |
| `page_count` | int | β | Number of pages to return, default 3, max 5 |
### Tool 6: `lookup_stock_code`
Diagnostic tool: look up stock_codeβstockId mapping. Use only when `download_hkexnews_announcement` / `list_hkexnews_announcements` returns `Stock code not found`. Bypasses the session failed-code cache.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `stock_code` | string | β
| 5-digit HK stock code, e.g. `"00700"` |
| `refresh` | bool | β | Force re-download of HKEX company mappings (default: `false`) |
## Configuration
On first run, a default config file is created at `~/.agentladle/mcp-hkexnews/config.yaml`:
```yaml
paths:
data_dir: "~/.agentladle/mcp-hkexnews/data"
pdf_dir: "~/.agentladle/mcp-hkexnews/data/pdf"
json_dir: "~/.agentladle/mcp-hkexnews/data/json"
download:
delay_between_requests: 0.3
min_file_size: 500
list_row_range: 100
list_max_pages: 5
company:
cache_ttl_days: 7
tiers:
cache_ttl_days: 7
```
## Data Directory Structure
```
~/.agentladle/mcp-hkexnews/
βββ config.yaml # Configuration (auto-created)
βββ data/
βββ companies.json # stock_codeβstockId mapping (auto-downloaded & cached)
βββ tiers.json # HKEX headline category mapping (auto-downloaded & cached)
βββ pdf/ # Downloaded announcement bundles
β βββ 00700_13500_2026-03-15_a1b2c3d4/
β β βββ primary.pdf
β β βββ manifest.json
β βββ ...
βββ json/ # Parsed page-split JSON
βββ 00700_13500_2026-03-15_a1b2c3d4.json
βββ ...
```
**File naming convention:** `{STOCK_CODE}_{T2_CODE}_{RELEASE_DATE}_{ID_HASH}`
## Example Usage
The tools are designed with an **EAFP (Easier to Ask for Forgiveness than Permission)** approach. AI assistants should attempt to retrieve data directly and rely on errors to trigger downloads.
**Scenario A: File already exists locally (Shortest Path)**
```
User: "Search 00700 inside information for buyback"
1. keyword_search(local_key="00700_50100_2026-07-09_a1b2c3d4", keywords=["buyback", "repurchase"])
β Returns page snippets matching the keywords immediately.
```
**Scenario B: File missing (Fallback triggered)**
```
User: "What did Tencent announce in its latest inside information?"
1. list_hkexnews_announcements(stock_code="00700", category="Inside Information", limit=3)
β Returns local_key / release_date / title.
2. keyword_search(local_key="...", keywords=["inside information"])
β Error: File not found.
3. download_hkexnews_announcement(stock_code="00700", local_key="...")
β Downloads PDF to ~/.agentladle/mcp-hkexnews/data/pdf/
4. parse_hkexnews_announcement(local_key="...")
β Parses into JSON.
5. keyword_search(local_key="...", keywords=["inside information"])
β Retries search and returns data.
```
## Tech Stack
| Component | Choice | Purpose |
|-----------|--------|---------|
| MCP Framework | `mcp` (FastMCP) | MCP server with stdio transport |
| HTTP Client | `httpx` | HKEXnews API requests & file downloads |
| PDF Parsing | `pymupdf` + `beautifulsoup4` | PDF page text extraction; HTML fallback |
| Search | Python built-in | TF + position-boost scoring |
| Config | `pyyaml` | YAML configuration file |
| Securities List | `openpyxl` | Parse HKEX ListOfSecurities.xlsx |
## Project Structure
```
src/mcp_hkexnews/
βββ __init__.py
βββ server.py # MCP Server entry point
βββ config.py # Config loading (~/.agentladle/mcp-hkexnews/config.yaml, singleton cached)
βββ models.py # Data models
βββ categories.py # Announcement category blacklist
βββ response.py # Unified JSON responses
βββ instances.py # Service singletons
βββ tools/
β βββ list_announcements.py # Tool 1: list_hkexnews_announcements
β βββ download.py # Tool 2: download_hkexnews_announcement
β βββ parse.py # Tool 3: parse_hkexnews_announcement
β βββ search.py # Tool 4: keyword_search
β βββ page.py # Tool 5: get_announcement_pages
β βββ lookup.py # Tool 6: lookup_stock_code
βββ services/
βββ company.py # HKEX activestock + ListOfSecurities + stock_codeβstockId
βββ tiers.py # HKEX tierone/tiertwo category cache
βββ downloader.py # HKEXnews titleSearch + PDF download
βββ parser.py # PDF/HTMLβJSON parsing (PyMuPDF)
βββ searcher.py # Local JSON search + TF scoring
βββ keys.py # local_key helpers
```
## License
MIT
TDQS
A4.7/5.0
Scored across 6 tools
Disambiguation5/5
Each tool has a distinct purpose: downloading, parsing, searching, listing, looking up stock codes, and retrieving pages. No overlap or ambiguity.
Naming Consistency5/5
All tool names follow a consistent verb_noun pattern with snake_case (e.g., download_hkexnews_announcement, list_hkexnews_announcements).
Tool Count5/5
6 tools is an appropriate number for the domain, covering the core workflows without being excessive or insufficient.
Completeness5/5
The tool surface covers the full lifecycle: listing, downloading, parsing, searching, and retrieving pages. No obvious gaps for the read-only nature of HKEX announcements.
Maintenance
ActivityMaintained
ResponsivenessNo issues