Summary
There is no query timeout. MYSQL_CONNECT_TIMEOUT (added in v2.0.7) covers establishing the connection, and MYSQL_QUEUE_LIMIT covers the pool queue — but once a query is running, nothing bounds it. If a SELECT takes longer than the caller is willing to wait, mysql_query simply never returns.
For an interactive MCP client that is merely slow. For an autonomous agent it is worse than an error: the tool call has no result, no partial output and no failure, so the agent sits on it indefinitely. In our case a sub-agent stayed "still running", reporting the same progress, until a human noticed and killed it. An error after N seconds would have been recoverable; silence is not.
Environment
@benborla29/mcp-server-mysql 2.0.9 (latest on npm at the time of writing, published 2026-06-19)
- launched via
npx, stdio transport
- read-only config:
ALLOW_INSERT_OPERATION / ALLOW_UPDATE_OPERATION / ALLOW_DELETE_OPERATION all false
What I checked before filing
Grepping the published dist/ for timeout-related identifiers returns only:
MYSQL_CONNECT_TIMEOUT
connectTimeout
So the gap looks structural rather than a misconfiguration on my side. The pool is created with waitForConnections: true, which means a hung query can also block subsequent callers until queueLimit is reached — one stuck query degrades the whole server, not just its own call.
Suggested fix
Something like MYSQL_QUERY_TIMEOUT (ms, 0/unset = current behaviour), applied per query. Two implementations that would both work:
- Server-side ceiling for
SELECT — inject the optimizer hint SELECT /*+ MAX_EXECUTION_TIME(ms) */ …. MySQL enforces it itself (5.7.8+), so the query is actually stopped rather than abandoned. Only applies to SELECT, which for a read-only server is the common case.
- Client-side race +
KILL QUERY — bound the promise and issue KILL QUERY <id> on expiry, so the connection is returned to the pool instead of leaking.
Either way the important part is that the tool call returns an error instead of hanging: an MCP client can retry, narrow the query, or tell the user. It cannot do anything with silence.
Workaround for anyone hitting this now
Write the hint into the query yourself:
SELECT /*+ MAX_EXECUTION_TIME(30000) */ ...
It is an optimizer hint, i.e. a comment, so a server that does not support it ignores it rather than failing. We enforce it with a client-side pre-tool hook that rejects any SELECT reaching the server without one — but that is a workaround around the server, and it only protects clients that happen to have such a hook.
Happy to test a patch against a production-sized dataset if that helps.
Summary
There is no query timeout.
MYSQL_CONNECT_TIMEOUT(added in v2.0.7) covers establishing the connection, andMYSQL_QUEUE_LIMITcovers the pool queue — but once a query is running, nothing bounds it. If aSELECTtakes longer than the caller is willing to wait,mysql_querysimply never returns.For an interactive MCP client that is merely slow. For an autonomous agent it is worse than an error: the tool call has no result, no partial output and no failure, so the agent sits on it indefinitely. In our case a sub-agent stayed "still running", reporting the same progress, until a human noticed and killed it. An error after N seconds would have been recoverable; silence is not.
Environment
@benborla29/mcp-server-mysql2.0.9 (latest on npm at the time of writing, published 2026-06-19)npx, stdio transportALLOW_INSERT_OPERATION/ALLOW_UPDATE_OPERATION/ALLOW_DELETE_OPERATIONallfalseWhat I checked before filing
Grepping the published
dist/for timeout-related identifiers returns only:So the gap looks structural rather than a misconfiguration on my side. The pool is created with
waitForConnections: true, which means a hung query can also block subsequent callers untilqueueLimitis reached — one stuck query degrades the whole server, not just its own call.Suggested fix
Something like
MYSQL_QUERY_TIMEOUT(ms,0/unset = current behaviour), applied per query. Two implementations that would both work:SELECT— inject the optimizer hintSELECT /*+ MAX_EXECUTION_TIME(ms) */ …. MySQL enforces it itself (5.7.8+), so the query is actually stopped rather than abandoned. Only applies toSELECT, which for a read-only server is the common case.KILL QUERY— bound the promise and issueKILL QUERY <id>on expiry, so the connection is returned to the pool instead of leaking.Either way the important part is that the tool call returns an error instead of hanging: an MCP client can retry, narrow the query, or tell the user. It cannot do anything with silence.
Workaround for anyone hitting this now
Write the hint into the query yourself:
It is an optimizer hint, i.e. a comment, so a server that does not support it ignores it rather than failing. We enforce it with a client-side pre-tool hook that rejects any
SELECTreaching the server without one — but that is a workaround around the server, and it only protects clients that happen to have such a hook.Happy to test a patch against a production-sized dataset if that helps.