# MCP 直连会话管理

## 会话过期症状

单个 MCP session（JSON-RPC 直连 `project.feishu.cn/mcp_server/v1`）在大批量分页查询（>30页）后，返回内容格式异常，regex/JSON解析失败。错误信息：
```
page 34 REGEX fail (session expired?)
'NoneType' object has no attribute 'group'
```

## 应对：分段拉取 + 会话重建

```python
def init_session():
    resp, sid = rpc_raw({"jsonrpc":"2.0","id":1,"method":"initialize",
        "params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"py","version":"1.0"}}})
    h = dict(HEADERS); h["Mcp-Session-Id"] = sid
    try: urllib.request.urlopen(urllib.request.Request(URL, data=json.dumps({"jsonrpc":"2.0","method":"notifications/initialized"}).encode(), headers=h, method="POST"), timeout=30).read()
    except: pass
    return sid

def pull_pages(start_page, end_page, sid):
    rows = []
    for page in range(start_page, end_page+1):
        offset = (page-1)*50
        resp, sid = rpc_raw(call, sid)  # call with search_by_mql
        if "error" in resp:
            if "session" in str(resp["error"]).lower():
                return None, sid  # signal session expired
            continue
        # ... parse rows ...
    return rows, sid

# Main: segment every 20 pages
sid = init_session()
for seg_start in range(1, 65, 20):
    seg_end = min(seg_start+19, 63)
    rows, sid = pull_pages(seg_start, seg_end, sid)
    if rows is None:
        sid = init_session()  # rebuild session
        rows, sid = pull_pages(seg_start, seg_end, sid)
```

## JSON 解析：raw_decode 替代 regex

```python
from json.decoder import JSONDecoder

content = resp["result"]["content"][0]["text"]
decoder = JSONDecoder()
inner, _ = decoder.raw_decode(content.strip())
if "result" in inner and isinstance(inner["result"], str):
    inner = json.loads(inner["result"])
rows = inner.get("data", {}).get("1", [])
```

**为什么不用 regex**：`re.search(r"\{.*\}", content, re.S)` 在 session 过期后返回的数据格式不匹配，返回 None → `.group()` 抛异常。

## 数据覆盖保护

```python
import shutil
if os.path.exists(output_path):
    shutil.copy2(output_path, output_path + ".bak")  # 写入前强制备份
```
