Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions i18n/de/pages/advanced/header-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
translation:
sections: [81862a209b483d27, b76e8073487afa03, bc214b2fc2bcdae4, d5835477c0b60163, a5d9786f902ad8e1]
tool: 1
---
# Header-Parameter {#header-parameters}

Die meisten Server brauchen das nie.

Ein Gateway oder Load Balancer vor deinem Server kann nur anhand dessen routen, was er lesen kann, ohne den Body zu parsen. Markiere ein Tool-Argument mit `x-mcp-header`, und Clients mit der **[Protokollversion](../protocol-versions.md)** `2026-07-28` senden seinen Wert zusätzlich als HTTP-Header.

## Ein Argument markieren {#mark-an-argument}

Die Markierung ist ein zusätzlicher Schlüssel im JSON Schema des Arguments. Bei `MCPServer` setzt `Field` ihn dort:

```python title="server.py" hl_lines="13"
--8<-- "docs_src/header_parameters/tutorial001.py"
```

* Über Streamable HTTP mit `2026-07-28` sendet ein Client `Mcp-Param-Region` zusätzlich zum Body, und der Server lehnt einen Aufruf ab, bei dem beide nicht übereinstimmen.
* Ein Client, der das Tool nicht aufgelistet hat, hat die Markierung nie gesehen: Er sendet keinen Header, und der Aufruf wird abgelehnt. Der `Client` dieses SDK listet dann die Tools auf und sendet den Aufruf einmal erneut. Vorher aufzulisten spart also nur einen Roundtrip.
* Jede andere Verbindung ignoriert die Annotation.

Deine Funktion ändert sich nicht: `region` kommt weiterhin als Argument an.

## Was sich markieren lässt {#what-can-be-marked}

Argumente vom Typ `str`, `int` und `bool`. Alles andere wird beim Registrieren des Tools mit `InvalidSignature` abgewiesen.

Das gilt auch für `str | None`, das keinen einzelnen Typ hat. Ein optionales Argument braucht ein ausgeschriebenes Schema, mit `WithJsonSchema` von Pydantic:

```python
region: Annotated[str | None, WithJsonSchema({"type": "string", "x-mcp-header": "Region"})] = None
```

## Beim Low-Level-`Server` {#on-the-low-level-server}

Dort schreibst du `input_schema` von Hand, der Schlüssel kommt also direkt hinein:

```python title="server.py" hl_lines="18"
--8<-- "docs_src/header_parameters/tutorial002.py"
```

* Nichts prüft die Annotation für dich: Eine ungültige wird ausgeliefert, und `2026-07-28`-Clients lassen das Tool aus ihrer Auflistung weg.

### Schemas nach Namen {#schemas-by-name}

Um den Header zu prüfen, braucht das SDK das Eingabeschema des Tools, bevor es den Aufruf weiterleitet. Ohne `get_tool_input_schema` holt es sich das Schema, indem es bei jedem Aufruf mit Argumenten deinen `on_list_tools`-Handler ausführt – egal, ob überhaupt ein Tool markiert ist.

```python title="server.py" hl_lines="26 39-41 48"
--8<-- "docs_src/header_parameters/tutorial003.py"
```

* Übergib die Funktion, um aus dem zu antworten, was du schon hast.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: um aus dem zu antworten is not idiomatic German and makes this instruction unclear. Use die auf Grundlage dessen antwortet, was du schon hast instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At i18n/de/pages/advanced/header-parameters.md, line 54:

<comment>`um aus dem zu antworten` is not idiomatic German and makes this instruction unclear. Use `die auf Grundlage dessen antwortet, was du schon hast` instead.</comment>

<file context>
@@ -0,0 +1,65 @@
+--8<-- "docs_src/header_parameters/tutorial003.py"
+```
+
+* Übergib die Funktion, um aus dem zu antworten, was du schon hast.
+* Gib `None` für ein Tool zurück, bei dem es nichts zu prüfen gibt.
+
</file context>
Suggested change
* Übergib die Funktion, um aus dem zu antworten, was du schon hast.
* Übergib die Funktion, die auf Grundlage dessen antwortet, was du schon hast.

* Gib `None` für ein Tool zurück, bei dem es nichts zu prüfen gibt.

## Zusammenfassung {#recap}

* `x-mcp-header` an einem Tool-Argument sorgt dafür, dass `2026-07-28`-Clients es als HTTP-Header `Mcp-Param-*` wiederholen.
* Der Server lehnt einen Aufruf ab, bei dem Header und Body nicht übereinstimmen.
* Nur Argumente vom Typ `str`, `int` und `bool` lassen sich markieren. Bei allem anderen löst `MCPServer` `InvalidSignature` aus.
* Der Low-Level-`Server` prüft nichts, und Clients verwerfen ein Tool mit ungültiger Annotation.
* `get_tool_input_schema` verhindert, dass der Low-Level-`Server` bei jedem Aufruf `on_list_tools` ausführt.

Der Rest der handgeschriebenen `Server`-API steht in **[Der Low-Level-Server](low-level-server.md)**.
4 changes: 3 additions & 1 deletion i18n/de/pages/advanced/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
translation:
sections: [ca6988b7503cd2d3]
sections: [348f8697c6b12cd0]
tool: 1
---
# Für Fortgeschrittene {#advanced}
Expand All @@ -14,6 +14,8 @@ von `MCPServer` im Weg ist:
eigene JSON-RPC-Methoden.
* **[Paginierung](pagination.md)** und **[Middleware](middleware.md)**: zwei Dinge, die
*nur* auf dem Low-Level-`Server` gehen.
* **[Header-Parameter](header-parameters.md)**: lassen ein Gateway einen Tool-Aufruf anhand
eines seiner Argumente routen.
* **[Erweiterungen](extensions.md)** und **[MCP Apps](apps.md)**: die
Erweiterungsfläche des Protokolls. Kombiniere Erweiterungspakete zu einem Server oder schreibe deine eigenen.

Expand Down
3 changes: 2 additions & 1 deletion i18n/de/pages/advanced/low-level-server.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
translation:
sections: [2c79b6338e09b7ac, 9d5d10a5f0405d0a, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, d50fe7faead8cf68, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, 8db7116fc8ddd0ee, ebc33704fbd74262, 0fde3bcea081ba3a]
sections: [2c79b6338e09b7ac, 9d5d10a5f0405d0a, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, d50fe7faead8cf68, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, 8db7116fc8ddd0ee, 2090d99b355bc2c7, 0fde3bcea081ba3a]
tool: 1
---
# Der Low-Level-Server {#the-low-level-server}
Expand Down Expand Up @@ -209,6 +209,7 @@ Jeder davon ist eine Idee, für die du jetzt das Vokabular hast; jeder hat seine
* `on_call_tool`, `on_get_prompt` und `on_read_resource` dürfen statt ihres normalen Ergebnisses ein `InputRequiredResult` zurückgeben, um den Aufruf anzuhalten und den Client um Eingaben zu bitten; siehe **[Multi-Roundtrip-Requests](../handlers/multi-round-trip.md)** (multi-round-trip requests). Getreu dieser Ebene wird nichts für dich installiert: Wo `MCPServer` `requestState` standardmäßig versiegelt, geht hier der `request_state`, den du setzt, genau so über die Leitung, wie du ihn geschrieben hast, bis du dich mit `server.middleware.append(RequestStateBoundary(RequestStateSecurity(keys=[...]), default_audience=server.name))` dafür entscheidest: eine Zeile (beide Namen lassen sich aus `mcp.server.request_state` importieren) für genau die Versiegelung und Verifizierung, die `MCPServer` vornimmt (**[`requestState` schützen](../handlers/multi-round-trip.md#protecting-requeststate)**).
* `on_list_resources`, `on_read_resource`, `on_list_prompts`, `on_get_prompt`, `on_completion` haben dieselbe Form `(ctx, params) -> result` für die anderen Primitive.
* `on_subscriptions_listen` bedient den Stream `subscriptions/listen` aus 2026-07-28. Übergib einen `ListenHandler`, der auf einem `SubscriptionBus` aufgebaut ist, und veröffentliche Ereignisse aus deinen anderen Handlern auf dem Bus; die vollständige Zusammensetzung steht in **[Abonnements](../handlers/subscriptions.md)**.
* `get_tool_input_schema` hält `on_list_tools` aus dem Aufrufpfad heraus; siehe **[Header-Parameter](header-parameters.md#schemas-by-name)**.
* `server.streamable_http_app()` gibt dieselbe Starlette-App zurück wie die von `MCPServer`; stelle sie bereit, wie **[Den Server betreiben](../run/index.md)** jede andere ASGI-App bereitstellt. Hier unten gibt es kein `server.run(transport=...)`: `server.run(read_stream, write_stream, server.create_initialization_options())` treibt eine Verbindung über ein Paar Streams, und diese eine Zeile ist alles.

## Zusammenfassung {#recap}
Expand Down
32 changes: 28 additions & 4 deletions i18n/de/pages/advanced/middleware.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
translation:
sections: [6048b4f308edbb8c, 46056f318ef205e4, c3e565b61acd75c5, c62422b159c6ed09, 420968f514138f43]
sections: [58e1103d9a323ccf, 46056f318ef205e4, 812b414557fb0c35, 4df162eea2518d38, c62422b159c6ed09, 420968f514138f43]
tool: 1
---
# Middleware {#middleware}
Expand All @@ -16,7 +16,7 @@ Du schreibst sie als `async (ctx, call_next)` und hängst sie an `server.middlew

`MCPServer` nimmt die Liste bei der Konstruktion entgegen (`MCPServer(name, middleware=[...])`) und stellt
sie als `mcp.middleware` bereit; der Low-Level-`Server` stellt dieselbe Liste als `server.middleware`
bereit. Das Beispiel unten verwendet den Low-Level-`Server`; wenn `Server(name, on_call_tool=...)` neu
bereit. Die Beispiele unten verwenden den Low-Level-`Server`; wenn `Server(name, on_call_tool=...)` neu
für dich ist, lies zuerst **[Der Low-Level-Server](low-level-server.md)**.

## Eine Timing-Middleware {#a-timing-middleware}
Expand Down Expand Up @@ -61,14 +61,38 @@ Genau darum geht es. Middleware umschließt **jede** eingehende Nachricht:
* Sogar eine Methode, für die der Server keinen Handler hat: `call_next` wirft den
`MCPError(-32601, "Method not found")` *durch* deine Middleware hindurch auf dem Weg zum Client.

## Eine Obergrenze für gleichzeitige Aufrufe {#a-concurrency-cap}

Eine Middleware muss `call_next(ctx)` nicht aufrufen. Wirf stattdessen einen `MCPError`, und diese eine
Nachricht wird **abgelehnt**: Die Verbindung bleibt bestehen, und die nächste Nachricht geht durch.

Angenommen, jede Suche belegt eine Verbindung aus einem Pool von vier. Diese Middleware lässt vier
Tool-Aufrufe gleichzeitig laufen und lehnt den fünften ab:

```python title="server.py" hl_lines="15-16 40-55 59"
--8<-- "docs_src/middleware/tutorial002.py"
```

* Gezählt wird nur `tools/call`. Der Server beantwortet `server/discover` und `tools/list` also
weiter, während er Tool-Aufrufe ablehnt.
* MCP definiert keinen Fehlercode für „Server ausgelastet“, also ist `SERVER_BUSY` ein eigener Code
dieses Servers.
* Das Ablehnen sagt dem Client sofort, dass der Server überlastet ist. Wenn du Aufrufer lieber warten
lässt, umschließe stattdessen `call_next(ctx)` mit einem `anyio.CapacityLimiter`.

Ein geworfener `MCPError` geht an die Client-Anwendung, nicht an das Modell. Soll das Modell die
Meldung lesen, gib stattdessen ein Tool-Ergebnis mit `is_error=True` zurück: Das ist **Antworten**,
weiter unten.

## Was du in einer Middleware tun kannst {#what-you-can-do-inside-one}

In aufsteigender Reihenfolge danach, wie sehr du zögern solltest:

* **Beobachten.** Miss es, zähle es, logge es. Das Beispiel oben.
* **Beobachten.** Miss es, zähle es, logge es. Die Timing-Middleware oben.
* **Ablehnen.** Wirf einen `MCPError` *statt* `call_next(ctx)` aufzurufen, und diese eine Nachricht
wird mit einem JSON-RPC-Fehler beantwortet. Die Verbindung bleibt bestehen; die nächste Nachricht
geht durch. So beschränkt ein Server `subscriptions/listen` pro Aufrufer:
geht durch. Die Obergrenze für gleichzeitige Aufrufe oben. So beschränkt ein Server auch
`subscriptions/listen` pro Aufrufer:
**[Entscheiden, wer zusehen darf](../handlers/subscriptions.md#deciding-who-may-watch)** auf der
Seite Abonnements führt es Schritt für Schritt vor.
* **Umschreiben.** `ctx` ist eine Dataclass: `await call_next(dataclasses.replace(ctx, params=...))`
Expand Down
5 changes: 3 additions & 2 deletions i18n/de/pages/client/identity-assertion.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
translation:
sections: [a91322c46111d16d, 8e6fd6d6f59bb568, e7828fd2729b2c9d, a03ec26bfc678b65, 1034c653c0bcf1b0]
sections: [a91322c46111d16d, 8e6fd6d6f59bb568, 7cf38181f6c99fd5, 37804d4fb36d6302, 1034c653c0bcf1b0]
tool: 1
---
# Identity Assertion {#identity-assertion}
Expand Down Expand Up @@ -66,7 +66,7 @@ Die Erweiterung verlangt das nicht; es ist eine bewusst strengere Entscheidung.

### Ein vertraulicher Client {#a-confidential-client}

`client_secret` ist erforderlich; ohne löst der Konstruktor einen `ValueError` aus. Das IETF-Profil unter [SEP-990](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/990) reserviert diesen Grant für vertrauliche Clients, SEP-990 verlangt, dass sich der Client authentifiziert, und dieses SDK setzt beides durch, indem es auf einem geteilten Secret besteht. `token_endpoint_auth_method` legt fest, wo es mitreist: `client_secret_post` (der Standardwert, im Formular-Body) oder `client_secret_basic` (ein HTTP-Basic-Header). Das Profil erlaubt außerdem `private_key_jwt`; dieser Provider unterstützt es nicht.
`client_secret` ist erforderlich; ohne löst der Konstruktor einen `ValueError` aus. Das IETF-Profil unter [SEP-990](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/990) empfiehlt diesen Grant nur für vertrauliche Clients, und [RFC 7521](https://datatracker.ietf.org/doc/html/rfc7521) überlässt diese Richtlinie dem Autorisierungsserver. Dieses SDK wählt auf beiden Seiten die vorsichtige Lesart: Der eingebaute Autorisierungsserver weist einen Client ab, der kein geteiltes Secret hat, und dieser Provider besteht auf einem. `token_endpoint_auth_method` legt fest, wo es mitreist: `client_secret_post` (der Standardwert, im Formular-Body) oder `client_secret_basic` (ein HTTP-Basic-Header). Das Profil erlaubt außerdem `private_key_jwt`; dieser Provider unterstützt es nicht.

!!! tip
Lies `client_secret` aus der Umgebung oder einem Secret-Manager, nie aus der Versionsverwaltung.
Expand All @@ -93,6 +93,7 @@ Das SDK kann aber auch selbst der Autorisierungsserver *sein*: `create_auth_rout

* `identity_assertion_enabled=True` schaltet alles frei. Ausgeschaltet – das ist der Standardwert – beantwortet `/token` diesen Grant mit `unsupported_grant_type`, selbst wenn du den Hook implementiert hast, und die Metadaten erwähnen ihn nicht. Eingeschaltet erhalten die Metadaten den Grant-Typ `jwt-bearer` und listen `urn:ietf:params:oauth:grant-profile:id-jag` in `authorization_grant_profiles_supported`, dem Feld, mit dem die Erweiterung Unterstützung bekannt gibt. (Der Client dieses SDK liest es nie: Er ist für genau einen Issuer eingerichtet und fragt einfach.)
* **`exchange_identity_assertion`** ist der Hook. Bevor er läuft, hat das SDK den Client authentifiziert, öffentliche Clients abgewiesen und Clients abgewiesen, deren Registrierung den Grant nicht aufführt. Du bekommst ein `IdentityAssertionParams` (die rohe `assertion`, die angeforderten `scopes` und `resource`) und gibst ein schlichtes `OAuthToken` zurück.
* Öffentliche Clients abzuweisen ist eine Richtlinie des SDK, keine Vorgabe der Spezifikation. Der eingebaute Server authentifiziert Clients nur per geteiltem Secret: Er unterstützt kein `private_key_jwt` und löst Client ID Metadata Documents noch nicht auf ([#1801](https://github.com/modelcontextprotocol/python-sdk/issues/1801)), deshalb kann ein Client, der sich über ein solches Dokument ausweist, diesen Grant hier nicht nutzen. Ein Deployment, das eine andere Richtlinie will, kann die `/token`-Route, die `create_auth_routes` zurückgibt, durch eine eigene ersetzen.
* Die dynamische Client-Registrierung lehnt diesen Grant ausnahmslos ab, deshalb bedient `get_client` hier einen von Hand eingerichteten Client. Ein ID-JAG-Client kann sich nicht selbst ins Leben registrieren.
* Die halbe Klasse besteht aus Ablehnungen. `OAuthAuthorizationServerProvider` ist der *ganze* Autorisierungsserver, also verlangt er auch den Authorization-Code-Flow; ein Server, der Personen zusätzlich anmeldet, implementiert diese Methoden wirklich, und dieser hier hat genau eine Tür.

Expand Down
25 changes: 22 additions & 3 deletions i18n/de/pages/client/transports.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
translation:
sections: [9cac816674181eb0, 7c157764133fea1f, 40b4916d82eaf1d4, 10d151f2cc75317f, 3d0832f39b0d7059, 92742ba36533633d, 0aeca6145e7bd302]
sections: [9cac816674181eb0, 5619e950d206e6c8, 40b4916d82eaf1d4, 10d151f2cc75317f, 3d0832f39b0d7059, 92742ba36533633d, 991c10e47fda2636]
tool: 1
---
# Client-Transporte {#client-transports}
Expand Down Expand Up @@ -44,23 +44,41 @@ Zwei Dinge fallen auf:
* Der `httpx2.AsyncClient` gehört dir, also betrittst und verlässt **du** ihn. Das SDK schließt nie einen Client, den es nicht selbst erzeugt hat.
* `streamable_http_client(url, http_client=...)` gibt einen Transport zurück, und `Client(transport)` nimmt ihn an wie alles andere auch.

Behalte das `timeout=` bei. Es ist dasselbe, das der SDK-eigene Client verwendet (30 Sekunden, 300 für Lesevorgänge); ein `httpx2.AsyncClient`, der ohne gebaut wird, bekommt den 5-Sekunden-Standardwert von `httpx2`, und ein Tool-Aufruf, der länger läuft, schlägt mit einem Read-Timeout fehl.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: „ein httpx2.AsyncClient, der ohne gebaut wird“ fehlt das Objekt von „ohne“ — der Satz ist ungrammatisch. Die Entsprechungen in den anderen Sprachen (z. B. es „construido sin uno“, ru „созданный без него“) führen das Bezugsobjekt korrekt an. Ergänze „ohne einen (Timeout)“.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At i18n/de/pages/client/transports.md, line 47:

<comment>„ein `httpx2.AsyncClient`, der ohne gebaut wird“ fehlt das Objekt von „ohne“ — der Satz ist ungrammatisch. Die Entsprechungen in den anderen Sprachen (z. B. es „construido sin uno“, ru „созданный без него“) führen das Bezugsobjekt korrekt an. Ergänze „ohne einen (Timeout)“.</comment>

<file context>
@@ -44,23 +44,41 @@ Zwei Dinge fallen auf:
 * Der `httpx2.AsyncClient` gehört dir, also betrittst und verlässt **du** ihn. Das SDK schließt nie einen Client, den es nicht selbst erzeugt hat.
 * `streamable_http_client(url, http_client=...)` gibt einen Transport zurück, und `Client(transport)` nimmt ihn an wie alles andere auch.
 
+Behalte das `timeout=` bei. Es ist dasselbe, das der SDK-eigene Client verwendet (30 Sekunden, 300 für Lesevorgänge); ein `httpx2.AsyncClient`, der ohne gebaut wird, bekommt den 5-Sekunden-Standardwert von `httpx2`, und ein Tool-Aufruf, der länger läuft, schlägt mit einem Read-Timeout fehl.
+
 Eine Anmerkung zu TLS: `httpx2` prüft Zertifikate gegen den Trust Store des Betriebssystems (über
</file context>
Suggested change
Behalte das `timeout=` bei. Es ist dasselbe, das der SDK-eigene Client verwendet (30 Sekunden, 300 für Lesevorgänge); ein `httpx2.AsyncClient`, der ohne gebaut wird, bekommt den 5-Sekunden-Standardwert von `httpx2`, und ein Tool-Aufruf, der länger läuft, schlägt mit einem Read-Timeout fehl.
Behalte das `timeout=` bei. Es ist dasselbe, das der SDK-eigene Client verwendet (30 Sekunden, 300 für Lesevorgänge); ein `httpx2.AsyncClient`, der ohne einen gebaut wird, bekommt den 5-Sekunden-Standardwert von `httpx2`, und ein Tool-Aufruf, der länger läuft, schlägt mit einem Read-Timeout fehl.


Eine Anmerkung zu TLS: `httpx2` prüft Zertifikate gegen den Trust Store des Betriebssystems (über
[`truststore`](https://pypi.org/project/truststore/)), nicht gegen eine mitgelieferte CA-Liste. In einer Umgebung ohne
nutzbaren System-CA-Store (manche minimalen Container) setzt du die Standard-Umgebungsvariablen `SSL_CERT_FILE`/`SSL_CERT_DIR`
oder übergibst deinem `httpx2.AsyncClient` ein explizites `verify=ssl_context`
(Hintergrund in
[`httpx` und `httpx-sse` durch `httpx2` ersetzt](../migration.md#httpx-and-httpx-sse-replaced-by-httpx2)).

### Größere SSE-Events {#larger-sse-events}

Übergib `max_sse_event_size`, wenn ein Server ein großes Tool-Ergebnis oder eine große Benachrichtigung in einem einzigen SSE-Event sendet:

```python title="client.py" hl_lines="6-9"
--8<-- "docs_src/client_transports/tutorial005.py"
```

Der Standardwert ist 1 MiB pro Event, gemessen in Bytes, bevor das Event geparst wird. Das Limit gilt für
POST-Responses, den GET-Stream und wiederaufgenommene Streams. Ein zu großes Event in einer POST-Response oder einem
wiederaufgenommenen Stream lässt diesen Request mit einem SSE-Fehler fehlschlagen. Beim GET-Stream im Hintergrund loggt
der Client den Fehler und startet den Stream neu. Setze `max_sse_event_size=None`, um die Obergrenze abzuschalten, wenn du dem
Server vertraust und größere Events brauchst. JSON-Responses sind nicht betroffen. Wenn du `ClientSessionGroup` verwendest, setze
dieselbe Option an `StreamableHttpParameters`.

!!! warning
`streamable_http_client` nahm früher `headers=` und `timeout=` direkt entgegen. Das tut er nicht mehr:
seine einzigen Parameter sind `url`, `http_client` und `terminate_on_close`. Greifst du aus
Seine Parameter sind `url`, `http_client`, `terminate_on_close` und `max_sse_event_size`. Greifst du aus
Gewohnheit zu `headers=`, bekommst du:

```text
TypeError: streamable_http_client() got an unexpected keyword argument 'headers'
```

Alles, was mit HTTP zu tun hat, lebt jetzt auf dem einen `httpx2.AsyncClient`, den du übergibst.
Header, Authentifizierung, Proxys und Timeouts leben auf dem einen `httpx2.AsyncClient`, den du übergibst.
`max_sse_event_size` gilt dagegen für die SSE-Reader des MCP-Transports.

!!! info
`httpx2` behält die vertraute `httpx`-API bei. Wenn du `httpx` kennst, weißt du hier also bereits, wie Auth,
Expand Down Expand Up @@ -137,6 +155,7 @@ Ein **Transport** ist ein beliebiger asynchroner Kontextmanager, der ein `(read,

* `Client("http://.../mcp")` (eine URL) verbindet über Streamable HTTP, den Produktions-Transport.
* Header, Auth, Proxys und Timeouts gehören auf einen `httpx2.AsyncClient`, den du an `streamable_http_client(url, http_client=...)` übergibst. Es gibt kein Keyword `headers=`.
* Verwende `streamable_http_client(url, max_sse_event_size=...)`, um das Byte-Limit für jedes SSE-Event zu ändern.
* Redirects wird nur innerhalb des eigenen Origins der URL gefolgt (ein Trailing-Slash-`307`/`308`), plus `http`→`https` auf demselben Host. Alles andere schlägt mit `Redirect to … not followed` fehl; konfiguriere die endgültige URL.
* stdio ist `Client(StdioServerParameters(...))`. Pack es nur dann selbst in `stdio_client(...)` ein, wenn du die stderr des Kindprozesses umleiten willst.
* Der Subprozess bekommt eine Umgebung per Allow-List, nicht deine; `env=` ergänzt sie.
Expand Down
Loading
Loading