agentscope-ai / agentscope-ai/QwenPaw
[Feature]: Support `tls_verify` and `ca_file` in MCP client configuration
- 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.