MCP: Mutex deadlocks the server after overlapping tools/call (convex 1.45.0)
Summary
The Convex MCP server (convex mcp start, CLI 1.45.0, which is current latest) deadlocks after overlapping tools/call requests. The first call completes; every later call hangs forever with no stderr, no error, and no timeout from the server itself. MCP clients (Cursor waits ~1 hour) look frozen.
This is not a misconfigured mcp.json. Serial calls work. Concurrent calls break the process until it is restarted.
Repro
// JSON-RPC over stdio to `npx convex mcp start`
// After initialize + notifications/initialized:
// send two tools/call "status" back-to-back (no wait), then a third after a few seconds.Observed: id 10 answers (~1.2s). ids 11 and 12 never answer until the process is killed. Zero stderr.
Cursor reproduces this routinely because it batches MCP tool calls and/or a parent agent plus a subagent share one user-convex server.
Cause
npm-packages/convex/src/cli/lib/utils/mutex.ts (bundled in dist/cli.bundle.cjs). makeServer serializes every tools/call through this mutex.
Buggy finally (1.45.0):
this.currentlyRunning = callback().finally(() => {
const nextCb = this.waiting.shift();
if (nextCb === undefined) {
this.currentlyRunning = null;
} else {
this.enqueueCallbackForMutex(nextCb); // currentlyRunning is still the settled promise
}
});
this.waiting.length = 0;Inside finally, currentlyRunning is still non-null, so the re-entrant enqueueCallbackForMutex takes the else branch and pushes nextCb back onto waiting. currentlyRunning is never set to null, so the lock is held forever.
this.waiting.length = 0 after starting the first job also drops any callbacks already queued in the same tick.
Suggested fix
this.currentlyRunning = callback().finally(() => {
const nextCb = this.waiting.shift();
this.currentlyRunning = null;
if (nextCb !== undefined) {
this.enqueueCallbackForMutex(nextCb);
}
});Drop this.waiting.length = 0. Setting currentlyRunning = null first makes the re-entrant enqueue take the run-now branch.
Verified locally against overlapping status calls: all three requests then complete (~1s each).
Environment
convexnpm 1.45.0 (npm view convex version→ 1.45.0)- OS: Windows 10
- Client: Cursor, stdio MCP (
npx -y convex@latest mcp start) - Auth:
~/.convex/config.jsonpresent;npx convex data/exportwork in seconds
Related but different: #273 (specific tools timeout / --limit flag). This hang is any tool, including status, once two calls overlap.
Source: get-convex/convex-backend