Metadata-Version: 2.5
Name: sthub-mcp
Version: 0.0.2
Summary: Read-only MCP server for querying the STHub sthub.stock_warning MySQL table
Requires-Python: >=3.10
Requires-Dist: mcp[cli]<2.0.0,>=1.0.0
Requires-Dist: pymysql>=1.1.0
Description-Content-Type: text/markdown

# sthub-mcp

Read-only MCP server for querying the [STHub](https://github.com/) `sthub.stock_warning` MySQL table — ST/*ST 警示板股票、重整套利、摘帽套利、立案调查、退市数据的自然语言查询入口。

面向的场景：日常想问"重整股中北京地区过去三年的有哪些"这种没有预先枚举好筛选维度的自由查询，现有 REST API 只支持固定筛选参数，覆盖不了。这个 MCP 直连数据库只读账号，让 Claude 自己理解 schema 并写 SQL 查询。

## Tools

| Tool | Description |
|------|-------------|
| `get_schema_guide` | 拉取线上真实列结构 + JSON 列内部字段路径说明 + 常见歧义点清单（**每次会话第一次查询前应该先调用**） |
| `get_query_recipes` | 常见查询场景的 SQL 模板 |
| `list_distinct_values` | 查某个维度列（province/city/industry/risk_sector/current_status/enterprise_type）的真实取值 |
| `run_readonly_query` | 执行一条只读 SELECT/WITH 查询 |

## 数据库账号准备（必须先做）

**不要复用后端 stservice 的读写账号。** 在 RDS 上新建一个只对 `stock_warning` 表有 `SELECT` 权限的独立账号：

```sql
CREATE USER 'sthub_mcp_ro'@'%' IDENTIFIED BY '<换成一个强密码>';
GRANT SELECT ON sthub.stock_warning TO 'sthub_mcp_ro'@'%';
FLUSH PRIVILEGES;
```

这是本 MCP 的主要安全边界——代码里的 SQL 校验（拒绝写操作关键字、拒绝多语句）只是第二道防线，真正防止误操作/越权访问的是这条 `GRANT SELECT` 本身只覆盖单表。

## Setup

```bash
claude mcp add sthub \
  -e STHUB_DB_HOST=<rds-host> \
  -e STHUB_DB_PORT=3306 \
  -e STHUB_DB_USER=sthub_mcp_ro \
  -e STHUB_DB_PASSWORD=<password> \
  -e STHUB_DB_NAME=sthub \
  -- uvx sthub-mcp
```

Or manually in your MCP config:

```json
{
  "mcpServers": {
    "sthub": {
      "command": "uvx",
      "args": ["sthub-mcp"],
      "env": {
        "STHUB_DB_HOST": "<rds-host>",
        "STHUB_DB_PORT": "3306",
        "STHUB_DB_USER": "sthub_mcp_ro",
        "STHUB_DB_PASSWORD": "<password>",
        "STHUB_DB_NAME": "sthub"
      }
    }
  }
}
```

See `.env.example` for the full list of environment variables.

## Usage Example

```
用户: 帮我查询重整股中北京地区过去三年的重整股有哪些

Claude 应该：
1. 调用 get_schema_guide() 了解真实列结构和 JSON 字段路径
2. 发现"地区"和"过去三年"存在歧义（顶层 province 列 vs JSON 内部 province 字段；
   自然年 vs 滚动三年；锚点日期用哪个事件），向用户澄清
3. 用 list_distinct_values("province") 确认"北京"在库里的真实存法
4. 用 run_readonly_query() 执行最终 SQL 并返回结果
```

## 安全设计

- DB 账号仅对 `stock_warning` 表有 `SELECT` 权限（见上）
- `run_readonly_query` 拒绝多语句、拒绝写操作/DDL/权限关键字，未指定 `LIMIT` 时自动加上（上限 500 行）
- 服务端查询执行超时 10 秒（`MAX_EXECUTION_TIME`）
- 出错返回结构化 JSON（`{"error": true, "message": ...}`）而非裸异常，方便 Claude 判断失败原因并调整查询

## Local Development

```bash
cd sthub-mcp
cp .env.example .env  # 填入只读账号凭据
npx @modelcontextprotocol/inspector uv run src/sthub_mcp/server.py
```
