agentscope-ai / agentscope-ai/QwenPaw

[Feature]: Support `tls_verify` and `ca_file` in MCP client configuration

Aperta
#4,175 3 commenti 0 reazioni 0 assegnatari Vedi su GitHub
enhancement
Lingua principale
Python
Stelle
34.9k
Fork
3.1k
Merge medio
1g 15h
PR unite (30g)
225

Descrizione

# Feature: Support `tls_verify` and `ca_file` in MCP client configuration

## Summary

Add `tls_verify` and `ca_file` fields to `MCPClientConfig` and wire them through to `httpx.AsyncClient(verify=...)`, so that `streamable_http` MCP clients can connect to servers using self-signed or private CA certificates.

## Component(s) Affected

- [x] Core / Backend (app, agents, config, providers, utils, local_models)
- [ ] Console (frontend web UI)
- [ ] Channels (DingTalk, Feishu, QQ, Discord, iMessage, etc.)
- [ ] Skills
- [ ] CLI
- [ ] Documentation (website)
- [ ] Tests
- [ ] CI/CD
- [ ] Scripts / Deploy

## Problem / Motivation

When running an MCP server behind HTTPS with a **self-signed certificate** (common in internal/development setups), the `streamable_http` transport fails because:

1. `MCPClientConfig` has **no `tls_verify` field** — the field is silently accepted via `populate_by_name=True` but never mapped to anything
2. `_build_client()` in `manager.py` creates `HttpStatefulClient` without passing any `verify` parameter
3. `HttpStatefulClient._setup_transport()` passes `**self.client_kwargs` to `httpx.AsyncClient()`, but nothing sets `verify=False`
4. So httpx uses its default SSL context, which **rejects self-signed certificates**

**Who benefits:**

- Developers running MCP servers with self-signed certs during prototyping/development
- Enterprise users with internal CA infrastructure
- Anyone deploying MCP servers on private networks without public CA-validated certificates

**Without this feature** the only workaround is to (a) install the self-signed cert into the system trust store, or (b) patch `httpx.AsyncClient(verify=False)` into QwenPaw's vendored `stateful_client.py` — both are fragile and environment-specific.

## Proposed Solution

### 1. Add fields to `MCPClientConfig` in `src/qwenpaw/config/config.py`

```python
class MCPClientConfig(BaseModel):
# ... existing fields ...
tls_verify: bool = True
ca_file: str = ""
```

- `tls_verify: True` means default SSL verification (backward-compatible)
- `tls_verify: False` disables SSL cert verification entirely (`httpx.AsyncClient(verify=False)`)
- `ca_file: "/path/to/ca.pem"` provides a custom CA bundle (`httpx.AsyncClient(verify="/path/to/ca.pem")`)
- When `ca_file` is set and `tls_verify` is `True`, use the CA file for verification

### 2. Wire through in `_build_client()` in `src/qwenpaw/app/mcp/manager.py`

```python
client_kwargs = {}
if not client_config.tls_verify:
client_kwargs["verify"] = False
elif client_config.ca_file:
client_kwargs["verify"] = client_config.ca_file

client = HttpStatefulClient(
name=client_config.name,
transport=client_config.transport,
url=client_config.url,
headers=headers or None,
**client_kwargs,
)
```

### 3. Leverage existing `**client_kwargs` plumbing

`HttpStatefulClient.__init__` already stores `**client_kwargs` as `self.client_kwargs`, and `_setup_transport()` already passes `**self.client_kwargs` to `httpx.AsyncClient()`. So the plumbing is already in place — the only missing piece is reading the config and passing the parameter.

### How it flows:

```
config.json
└── mcp.clients.dsm.tls_verify: false


MCPClientConfig (config.py)
└── tls_verify = client_config.tls_verify


manager.py._build_client()
└── verify=client_config.tls_verify


HttpStatefulClient.__init__(**client_kwargs)
└── self.client_kwargs = client_kwargs


_stream_http_transport()
└── httpx.AsyncClient(**self.client_kwargs, ...)


httpx respects verify=False → skips SSL verification ✓
```

## Alternatives Considered

| Approach | Pros | Cons |
|---|---|---|
| **System trust store** — install cert globally | Works for all tools | Requires sudo; environment-dependent; breaks on new machines |
| **Vendor patch** — hardcode `verify=False` in `stateful_client.py` | Quick fix | Fragile; lost on library update; not configurable per-client |
| **Env var** — read `SSL_CERT_FILE` / `REQUESTS_CA_BUNDLE` | No code change | httpx doesn't auto-read these; still no per-client control |
| **This proposal** — config-driven | Per-client control; backward-compatible; keeps config.json as single source of truth | Small code change needed |

## Additional Context

**Example config** for connecting to a self-signed MCP server:

```json
{
"mcp": {
"clients": {
"dsm": {
"name": "dsm",
"enabled": true,
"transport": "streamable_http",
"url": "https://192.168.31.226:8002/mcp",
"headers": {
"Authorization": "Bearer "
},
"tls_verify": false
}
}
}
}
```

**Relevant source locations (main branch):**

- `src/qwenpaw/config/config.py` — `MCPClientConfig`, line ~1170
- `src/qwenpaw/app/mcp/manager.py` — `_build_client()`, line ~247
- `src/qwenpaw/app/mcp/stateful_client.py` — `HttpStatefulClient.__init__`, line ~533 and `_setup_transport`, line ~609

**Note on default semantics:** `tls_verify` defaults to `True` to preserve existing behavior — only users who opt in (by setting `"tls_verify": false`) see any change.

## Willing to Contribute

- [ ] I am willing to open a PR for this feature (after discussion).

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.