Skip to content

Commit 6d8a4df

Browse files
assadyousufDouweM
andauthored
Update MCP docs to show you can pass SSL/TLS options via the http_client arg (#2214)
Co-authored-by: Douwe Maan <[email protected]>
1 parent fc6a2b2 commit 6d8a4df

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

docs/mcp/client.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,54 @@ calculator_server = MCPServerSSE(
235235
agent = Agent('openai:gpt-4o', toolsets=[weather_server, calculator_server])
236236
```
237237

238+
## Custom TLS / SSL configuration
239+
240+
In some environments you need to tweak how HTTPS connections are established –
241+
for example to trust an internal Certificate Authority, present a client
242+
certificate for **mTLS**, or (during local development only!) disable
243+
certificate verification altogether.
244+
All HTTP-based MCP client classes
245+
([`MCPServerStreamableHTTP`][pydantic_ai.mcp.MCPServerStreamableHTTP] and
246+
[`MCPServerSSE`][pydantic_ai.mcp.MCPServerSSE]) expose an `http_client`
247+
parameter that lets you pass your own pre-configured
248+
[`httpx.AsyncClient`](https://www.python-httpx.org/async/).
249+
250+
```python {title="mcp_custom_tls_client.py" py="3.10"}
251+
import httpx
252+
import ssl
253+
254+
from pydantic_ai import Agent
255+
from pydantic_ai.mcp import MCPServerSSE
256+
257+
258+
# Trust an internal / self-signed CA
259+
ssl_ctx = ssl.create_default_context(cafile="/etc/ssl/private/my_company_ca.pem")
260+
261+
# OPTIONAL: if the server requires **mutual TLS** load your client certificate
262+
ssl_ctx.load_cert_chain(certfile="/etc/ssl/certs/client.crt", keyfile="/etc/ssl/private/client.key",)
263+
264+
http_client = httpx.AsyncClient(
265+
verify=ssl_ctx,
266+
timeout=httpx.Timeout(10.0),
267+
)
268+
269+
server = MCPServerSSE(
270+
url="http://localhost:3001/sse",
271+
http_client=http_client, # (1)!
272+
)
273+
agent = Agent("openai:gpt-4o", toolsets=[server])
274+
275+
async def main():
276+
async with agent:
277+
result = await agent.run('How many days between 2000-01-01 and 2025-03-18?')
278+
print(result.output)
279+
#> There are 9,208 days between January 1, 2000, and March 18, 2025.
280+
```
281+
282+
1. When you supply `http_client`, Pydantic AI re-uses this client for every
283+
request. Anything supported by **httpx** (`verify`, `cert`, custom
284+
proxies, timeouts, etc.) therefore applies to all MCP traffic.
285+
238286
## MCP Sampling
239287

240288
!!! info "What is MCP Sampling?"

tests/test_examples.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import re
66
import shutil
7+
import ssl
78
import sys
89
from collections.abc import AsyncIterator, Iterable, Sequence
910
from dataclasses import dataclass
@@ -129,6 +130,10 @@ def test_docs_examples( # noqa: C901
129130
mocker.patch('random.randint', return_value=4)
130131
mocker.patch('rich.prompt.Prompt.ask', side_effect=rich_prompt_ask)
131132

133+
# Avoid filesystem access when examples call ssl.create_default_context(cafile=...) with non-existent paths
134+
mocker.patch('ssl.create_default_context', return_value=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT))
135+
mocker.patch('ssl.SSLContext.load_cert_chain', return_value=None)
136+
132137
class CustomEvaluationReport(EvaluationReport):
133138
def print(self, *args: Any, **kwargs: Any) -> None:
134139
if 'width' in kwargs: # pragma: lax no cover

0 commit comments

Comments
 (0)