Skip to content

No query timeout: a slow SELECT never returns and the MCP client hangs forever #148

Description

@ticketplus-bot

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:

  1. 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.
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions