MCP client has no httpx timeout, so the event stream fails after 5 seconds
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Anfängerfreundlichkeit
- 78/100
Rechercherichtung
Beginnen Sie in src/azure_functions_agents/discovery/mcp.py bei _build_http_client und prüfen Sie, wie sich der benutzerdefinierte httpx.AsyncClient vom MCP SDK-Client unterscheidet. Fügen Sie ein explizites Timeout hinzu, das für den langlebigen Event-Stream geeignet ist, und überprüfen Sie anschließend, dass sich authentifizierte MCP-Sitzungen initialisieren und einen Tool-Aufruf ohne den fünfsekündigen ReadTimeout abschließen können.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Summary
_build_http_client in src/azure_functions_agents/discovery/mcp.py creates its httpx.AsyncClient with no timeout. Thus httpx applies its 5 second default to all operations, including the long-lived MCP GET event stream. The stream then fails after 5 seconds, and the MCP session cannot start.
Affected code
src/azure_functions_agents/discovery/mcp.py, lines 88-99:
def _build_http_client(header_provider: Any) -> Any:
if header_provider is None:
return None
from httpx import AsyncClient
async def inject_headers(request: Any) -> None:
headers = await asyncio.to_thread(header_provider, {})
for key, value in headers.items():
request.headers[key] = value
return AsyncClient(follow_redirects=True, event_hooks={"request": [inject_headers]})
The MCP Python SDK builds its own client with sse_read_timeout=300. This client replaces it and loses that value.
Blast radius
_build_http_client returns None when header_provider is None. _build_header_provider returns a provider when the server entry has headers or auth.
Thus every authenticated mcp.json entry hits this defect:
- static headers, for example an Azure Functions system key in
x-functions-key - Entra authentication through
auth.scopeandauth.client_id - both together
Only an unauthenticated mcp.json entry avoids the defect, because then the SDK default client stays in use.
Symptoms
On the MCP server:
GET stream disconnected, reconnecting in 1000ms...
This message repeats.
On the MCP client (the agent app):
MCP server failed to initialize: Cancelled via cancel scope <id>
or:
('Failed to enter context manager.', BrokenResourceError())
or the chat request does not answer at all. A built-in chat API call returns HTTP 500, or it hangs for more than 240 seconds.
The logs do not show the true cause, because the cancel scope message hides the ReadTimeout.
The failure comes late, and it looks like an authentication fault
This is the most confusing part of the defect. Every earlier step succeeds:
| Step | Result |
|---|---|
| Deployment of both apps | Succeeds |
| Host start and function indexing on both apps | Succeeds |
MCP server discovery from mcp.json |
Succeeds |
| HTTP request to the MCP endpoint | Succeeds. The server answers 200 and runs the handler |
| MCP session start and tool call | Fails after about 5 seconds |
Thus nothing looks wrong until an agent tries to use a tool. The error text
names a cancel scope, so a reader first suspects the authentication
configuration, the system key, or the Entra token. It is none of these. The
server-side logs show that the request passed all authentication layers and
that the handler completed.
The ReadTimeout happens on the long-lived GET event stream, not on the POST
that carries the tool call. That is why the request appears to succeed while
the session still fails.
How to reproduce
- Deploy two Azure Functions apps with
azurefunctions-agents-runtime==0.1.0b15. One app exposes an agent throughbuiltin_endpoints.mcp. The other app refers to it inmcp.json. - Give the
mcp.jsonentry an authentication block. Either form is enough:
{
"servers": {
"worker": {
"type": "http",
"url": "https://<host>/runtime/webhooks/mcp",
"headers": { "x-functions-key": "<system key>" }
}
}
}
- Send a chat request to the client app. The request fails as shown above.
Isolated proof
An A/B test separates the transport from all other parts. The test starts the same MCP session twice against the same server, with the same headers. Only the client differs:
| httpx client | Result |
|---|---|
| MCP SDK default | SUCCESS — initialize, tools/list, and tools/call all complete |
Same shape as _build_http_client |
FAILED ReadTimeout -> Cancelled via cancel scope <id>; reason: deadline exceeded |
The failure string of the second row matches the string that the deployed app writes to Application Insights.
The server-side logs also show that the server handler runs and completes. Thus authentication is correct, and the fault is in the client transport only.
Suggested fix
Give the client an explicit timeout. Keep the read budget near the SDK value of sse_read_timeout=300:
from httpx import AsyncClient, Timeout
return AsyncClient(
follow_redirects=True,
timeout=Timeout(300.0, connect=30.0),
event_hooks={"request": [inject_headers]},
)
A configurable value in the mcp.json entry would also help, but a safe default is the important part.
Workaround
Applications can replace the function before they build the app. The replacement must run before create_function_app(), because create_function_app() calls discover_mcp_servers(), and the result goes into _DISCOVERED_MCP_SERVERS_CACHE:
from azure_functions_agents import create_function_app
from azure_functions_agents.discovery import mcp as _mcp_discovery
_mcp_discovery._build_http_client = _build_http_client_with_timeout
app = create_function_app()
This workaround uses a private name. Applications that use it must pin the runtime version.
Environment
azurefunctions-agents-runtime[monitor]==0.1.0b15- Python 3.13
- Azure Functions Flex Consumption (FC1), Linux, Python 3.13 worker
- Two Function Apps, MCP over HTTP between them
- Vorherrschende Sprache
- Python
- Sterne
- 9
- Forks
- 7
- Ø Merge
- 2 T. 7 Std.
- Gemergte PRs (30 T.)
- 21
Beitragsleitfaden
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus Azure/azure-functions-agents-runtime
-
MCP server headers: unresolved `$VAR` placeholders are sent verbatim instead of failing closed Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
-
bug
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 74/100
Azure/azure-functions-agents-runtime#223 · 1 Kommentar ·
-
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 38/100
Alle Issues in Azure/azure-functions-agents-runtime
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
-
enhancement
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
PolicyEngine/policyengine-us#9559 ·